@leg3ndy/otto-bridge 0.8.3 → 0.8.5
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/executors/native_macos.js +757 -0
- package/dist/types.js +1 -1
- package/package.json +1 -1
|
@@ -778,6 +778,46 @@ function noteBodyToHtml(text) {
|
|
|
778
778
|
.map((block) => `<p>${escapeHtml(block).replace(/\n/g, "<br>")}</p>`);
|
|
779
779
|
return blocks.join("");
|
|
780
780
|
}
|
|
781
|
+
function formatBytesCompact(bytes) {
|
|
782
|
+
if (!Number.isFinite(bytes) || !bytes || bytes <= 0) {
|
|
783
|
+
return "0 B";
|
|
784
|
+
}
|
|
785
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
786
|
+
let value = Number(bytes);
|
|
787
|
+
let unitIndex = 0;
|
|
788
|
+
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
789
|
+
value /= 1024;
|
|
790
|
+
unitIndex += 1;
|
|
791
|
+
}
|
|
792
|
+
const fractionDigits = value >= 10 || unitIndex === 0 ? 0 : 1;
|
|
793
|
+
return `${value.toFixed(fractionDigits)} ${units[unitIndex]}`;
|
|
794
|
+
}
|
|
795
|
+
function roundMetric(value, decimals = 0) {
|
|
796
|
+
if (!Number.isFinite(value)) {
|
|
797
|
+
return 0;
|
|
798
|
+
}
|
|
799
|
+
const factor = 10 ** decimals;
|
|
800
|
+
return Math.round(value * factor) / factor;
|
|
801
|
+
}
|
|
802
|
+
function parseScaledBytes(rawValue, rawUnit) {
|
|
803
|
+
const value = Number(rawValue);
|
|
804
|
+
if (!Number.isFinite(value)) {
|
|
805
|
+
return 0;
|
|
806
|
+
}
|
|
807
|
+
const normalizedUnit = rawUnit.trim().toUpperCase();
|
|
808
|
+
const multipliers = {
|
|
809
|
+
B: 1,
|
|
810
|
+
K: 1024,
|
|
811
|
+
KB: 1024,
|
|
812
|
+
M: 1024 ** 2,
|
|
813
|
+
MB: 1024 ** 2,
|
|
814
|
+
G: 1024 ** 3,
|
|
815
|
+
GB: 1024 ** 3,
|
|
816
|
+
T: 1024 ** 4,
|
|
817
|
+
TB: 1024 ** 4,
|
|
818
|
+
};
|
|
819
|
+
return Math.round(value * (multipliers[normalizedUnit] || 1));
|
|
820
|
+
}
|
|
781
821
|
function isSafeShellCommand(command) {
|
|
782
822
|
const trimmed = command.trim();
|
|
783
823
|
if (!trimmed) {
|
|
@@ -899,6 +939,39 @@ function parseStructuredActions(job) {
|
|
|
899
939
|
});
|
|
900
940
|
continue;
|
|
901
941
|
}
|
|
942
|
+
if (type === "browser_context") {
|
|
943
|
+
actions.push({
|
|
944
|
+
type: "browser_context",
|
|
945
|
+
app: asString(action.app) || asString(action.application) || undefined,
|
|
946
|
+
include_text: action.include_text === true,
|
|
947
|
+
include_tabs: action.include_tabs === true,
|
|
948
|
+
});
|
|
949
|
+
continue;
|
|
950
|
+
}
|
|
951
|
+
if (type === "app_status") {
|
|
952
|
+
actions.push({
|
|
953
|
+
type: "app_status",
|
|
954
|
+
app: asString(action.app) || asString(action.application) || undefined,
|
|
955
|
+
include_frontmost: action.include_frontmost === true,
|
|
956
|
+
include_running_apps: action.include_running_apps === true,
|
|
957
|
+
include_top_processes: action.include_top_processes === true,
|
|
958
|
+
});
|
|
959
|
+
continue;
|
|
960
|
+
}
|
|
961
|
+
if (type === "filesystem_inspect") {
|
|
962
|
+
const targetPath = asString(action.path);
|
|
963
|
+
if (targetPath) {
|
|
964
|
+
const rawLimit = Number(action.limit);
|
|
965
|
+
actions.push({
|
|
966
|
+
type: "filesystem_inspect",
|
|
967
|
+
path: targetPath,
|
|
968
|
+
include_children: action.include_children === true,
|
|
969
|
+
include_preview: action.include_preview === true,
|
|
970
|
+
limit: Number.isFinite(rawLimit) ? Math.max(1, Math.min(Math.round(rawLimit), 20)) : undefined,
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
continue;
|
|
974
|
+
}
|
|
902
975
|
if (type === "read_file" || type === "read_local_file") {
|
|
903
976
|
const filePath = asString(action.path);
|
|
904
977
|
if (filePath) {
|
|
@@ -922,6 +995,19 @@ function parseStructuredActions(job) {
|
|
|
922
995
|
actions.push({ type: "count_files", path: filePath, extensions, recursive });
|
|
923
996
|
continue;
|
|
924
997
|
}
|
|
998
|
+
if (type === "system_status") {
|
|
999
|
+
const validSections = Array.isArray(action.sections)
|
|
1000
|
+
? action.sections
|
|
1001
|
+
.map((item) => asString(item)?.toLowerCase())
|
|
1002
|
+
.filter((item) => item === "cpu" || item === "memory" || item === "disk" || item === "battery")
|
|
1003
|
+
: undefined;
|
|
1004
|
+
actions.push({
|
|
1005
|
+
type: "system_status",
|
|
1006
|
+
sections: validSections && validSections.length > 0 ? Array.from(new Set(validSections)) : undefined,
|
|
1007
|
+
include_top_processes: action.include_top_processes === true,
|
|
1008
|
+
});
|
|
1009
|
+
continue;
|
|
1010
|
+
}
|
|
925
1011
|
if (type === "run_shell" || type === "shell" || type === "terminal") {
|
|
926
1012
|
const command = asString(action.command) || asString(action.cmd);
|
|
927
1013
|
const cwd = asString(action.cwd);
|
|
@@ -1277,6 +1363,30 @@ export class NativeMacOSJobExecutor {
|
|
|
1277
1363
|
completionNotes.push(`Li a pagina ${page.title || page.url || "ativa"} no navegador.`);
|
|
1278
1364
|
continue;
|
|
1279
1365
|
}
|
|
1366
|
+
if (action.type === "browser_context") {
|
|
1367
|
+
await reporter.progress(progressPercent, "Lendo o contexto do navegador ativo");
|
|
1368
|
+
const browserContext = await this.collectBrowserContext(action.app, action.include_text === true, action.include_tabs === true);
|
|
1369
|
+
resultPayload.browser_context = browserContext;
|
|
1370
|
+
resultPayload.summary = browserContext.summary;
|
|
1371
|
+
completionNotes.push(browserContext.summary);
|
|
1372
|
+
continue;
|
|
1373
|
+
}
|
|
1374
|
+
if (action.type === "app_status") {
|
|
1375
|
+
await reporter.progress(progressPercent, "Lendo os apps ativos do Mac");
|
|
1376
|
+
const appStatus = await this.collectAppStatus(action.app, action.include_frontmost === true, action.include_running_apps === true, action.include_top_processes === true);
|
|
1377
|
+
resultPayload.app_status = appStatus;
|
|
1378
|
+
resultPayload.summary = appStatus.summary;
|
|
1379
|
+
completionNotes.push(appStatus.summary);
|
|
1380
|
+
continue;
|
|
1381
|
+
}
|
|
1382
|
+
if (action.type === "filesystem_inspect") {
|
|
1383
|
+
await reporter.progress(progressPercent, `Inspecionando ${action.path}`);
|
|
1384
|
+
const filesystem = await this.inspectFilesystemPath(action.path, action.include_children === true, action.include_preview === true, action.limit);
|
|
1385
|
+
resultPayload.filesystem = filesystem;
|
|
1386
|
+
resultPayload.summary = filesystem.summary;
|
|
1387
|
+
completionNotes.push(filesystem.summary);
|
|
1388
|
+
continue;
|
|
1389
|
+
}
|
|
1280
1390
|
if (action.type === "read_file") {
|
|
1281
1391
|
await reporter.progress(progressPercent, `Lendo ${action.path}`);
|
|
1282
1392
|
const fileContent = await this.readLocalFile(action.path, action.max_chars);
|
|
@@ -1301,6 +1411,14 @@ export class NativeMacOSJobExecutor {
|
|
|
1301
1411
|
};
|
|
1302
1412
|
continue;
|
|
1303
1413
|
}
|
|
1414
|
+
if (action.type === "system_status") {
|
|
1415
|
+
await reporter.progress(progressPercent, "Lendo CPU, memoria, disco e bateria do Mac");
|
|
1416
|
+
const systemStatus = await this.collectSystemStatus(action.sections, action.include_top_processes === true);
|
|
1417
|
+
resultPayload.system_status = systemStatus;
|
|
1418
|
+
resultPayload.summary = systemStatus.summary;
|
|
1419
|
+
completionNotes.push(systemStatus.summary);
|
|
1420
|
+
continue;
|
|
1421
|
+
}
|
|
1304
1422
|
if (action.type === "run_shell") {
|
|
1305
1423
|
await reporter.progress(progressPercent, `Rodando comando local: ${action.command}`);
|
|
1306
1424
|
const shellOutput = await this.runShellCommand(action.command, action.cwd);
|
|
@@ -1861,6 +1979,406 @@ end tell
|
|
|
1861
1979
|
}
|
|
1862
1980
|
return null;
|
|
1863
1981
|
}
|
|
1982
|
+
async resolveBrowserContextApp(preferredApp) {
|
|
1983
|
+
const candidates = [
|
|
1984
|
+
preferredApp || null,
|
|
1985
|
+
this.lastActiveApp,
|
|
1986
|
+
await this.getFrontmostAppName(),
|
|
1987
|
+
];
|
|
1988
|
+
for (const candidate of candidates) {
|
|
1989
|
+
if (candidate === "Safari" || candidate === "Google Chrome") {
|
|
1990
|
+
return candidate;
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
return null;
|
|
1994
|
+
}
|
|
1995
|
+
async readCurrentBrowserMetadata(app) {
|
|
1996
|
+
const script = app === "Google Chrome"
|
|
1997
|
+
? `
|
|
1998
|
+
tell application "Google Chrome"
|
|
1999
|
+
activate
|
|
2000
|
+
if (count of windows) = 0 then error "Google Chrome nao possui janelas abertas."
|
|
2001
|
+
set pageTitle to title of active tab of front window
|
|
2002
|
+
set pageUrl to URL of active tab of front window
|
|
2003
|
+
end tell
|
|
2004
|
+
return pageTitle & linefeed & pageUrl
|
|
2005
|
+
`
|
|
2006
|
+
: `
|
|
2007
|
+
tell application "Safari"
|
|
2008
|
+
activate
|
|
2009
|
+
if (count of windows) = 0 then error "Safari nao possui janelas abertas."
|
|
2010
|
+
set pageTitle to name of current tab of front window
|
|
2011
|
+
set pageUrl to URL of current tab of front window
|
|
2012
|
+
end tell
|
|
2013
|
+
return pageTitle & linefeed & pageUrl
|
|
2014
|
+
`;
|
|
2015
|
+
const { stdout } = await this.runCommandCapture("osascript", ["-e", script]);
|
|
2016
|
+
const [title, url] = String(stdout || "").split("\n");
|
|
2017
|
+
return {
|
|
2018
|
+
title: String(title || "").trim(),
|
|
2019
|
+
url: String(url || "").trim(),
|
|
2020
|
+
};
|
|
2021
|
+
}
|
|
2022
|
+
async readCurrentBrowserPage(app) {
|
|
2023
|
+
if (app === "Safari") {
|
|
2024
|
+
const page = await this.readFrontmostPage("Safari");
|
|
2025
|
+
return {
|
|
2026
|
+
title: page.title,
|
|
2027
|
+
url: page.url,
|
|
2028
|
+
text: page.text,
|
|
2029
|
+
};
|
|
2030
|
+
}
|
|
2031
|
+
const script = `
|
|
2032
|
+
tell application "Google Chrome"
|
|
2033
|
+
activate
|
|
2034
|
+
if (count of windows) = 0 then error "Google Chrome nao possui janelas abertas."
|
|
2035
|
+
delay 0.5
|
|
2036
|
+
set jsCode to "(function(){const title=document.title||'';const url=location.href||'';const text=((document.body&&document.body.innerText)||'').trim().slice(0,12000);return JSON.stringify({title,url,text});})();"
|
|
2037
|
+
set pageJson to execute active tab of front window javascript jsCode
|
|
2038
|
+
end tell
|
|
2039
|
+
return pageJson
|
|
2040
|
+
`;
|
|
2041
|
+
try {
|
|
2042
|
+
const { stdout } = await this.runCommandCapture("osascript", ["-e", script]);
|
|
2043
|
+
const parsed = JSON.parse(String(stdout || "").trim() || "{}");
|
|
2044
|
+
return {
|
|
2045
|
+
title: asString(parsed.title) || "",
|
|
2046
|
+
url: asString(parsed.url) || "",
|
|
2047
|
+
text: asString(parsed.text) || "",
|
|
2048
|
+
};
|
|
2049
|
+
}
|
|
2050
|
+
catch {
|
|
2051
|
+
const metadata = await this.readCurrentBrowserMetadata(app);
|
|
2052
|
+
return {
|
|
2053
|
+
...metadata,
|
|
2054
|
+
text: "",
|
|
2055
|
+
};
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
async listBrowserTabs(app, limit = 8) {
|
|
2059
|
+
const script = app === "Google Chrome"
|
|
2060
|
+
? `
|
|
2061
|
+
tell application "Google Chrome"
|
|
2062
|
+
if (count of windows) = 0 then return ""
|
|
2063
|
+
set activeTabId to id of active tab of front window
|
|
2064
|
+
set outputLines to ""
|
|
2065
|
+
repeat with t in tabs of front window
|
|
2066
|
+
set marker to "0"
|
|
2067
|
+
if (id of t) is activeTabId then set marker to "1"
|
|
2068
|
+
set outputLines to outputLines & marker & tab & (title of t) & tab & (URL of t) & linefeed
|
|
2069
|
+
end repeat
|
|
2070
|
+
return outputLines
|
|
2071
|
+
end tell
|
|
2072
|
+
`
|
|
2073
|
+
: `
|
|
2074
|
+
tell application "Safari"
|
|
2075
|
+
if (count of windows) = 0 then return ""
|
|
2076
|
+
set activeIndex to index of current tab of front window
|
|
2077
|
+
set outputLines to ""
|
|
2078
|
+
set tabIndex to 0
|
|
2079
|
+
repeat with t in tabs of front window
|
|
2080
|
+
set tabIndex to tabIndex + 1
|
|
2081
|
+
set marker to "0"
|
|
2082
|
+
if tabIndex is activeIndex then set marker to "1"
|
|
2083
|
+
set outputLines to outputLines & marker & tab & (name of t) & tab & (URL of t) & linefeed
|
|
2084
|
+
end repeat
|
|
2085
|
+
return outputLines
|
|
2086
|
+
end tell
|
|
2087
|
+
`;
|
|
2088
|
+
try {
|
|
2089
|
+
const { stdout } = await this.runCommandCapture("osascript", ["-e", script]);
|
|
2090
|
+
return String(stdout || "")
|
|
2091
|
+
.split(/\r?\n/)
|
|
2092
|
+
.map((line) => line.trim())
|
|
2093
|
+
.filter(Boolean)
|
|
2094
|
+
.slice(0, limit)
|
|
2095
|
+
.map((line) => {
|
|
2096
|
+
const [marker, title, url] = line.split("\t");
|
|
2097
|
+
return {
|
|
2098
|
+
current: marker === "1",
|
|
2099
|
+
title: String(title || "").trim(),
|
|
2100
|
+
url: String(url || "").trim(),
|
|
2101
|
+
};
|
|
2102
|
+
});
|
|
2103
|
+
}
|
|
2104
|
+
catch {
|
|
2105
|
+
return [];
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
buildBrowserContextSummary(context, includeText, includeTabs) {
|
|
2109
|
+
const title = context.title || "sem titulo visivel";
|
|
2110
|
+
let summary = `${context.app} esta na aba "${title}"`;
|
|
2111
|
+
if (context.url) {
|
|
2112
|
+
summary += ` com a URL ${context.url}.`;
|
|
2113
|
+
}
|
|
2114
|
+
else {
|
|
2115
|
+
summary += ".";
|
|
2116
|
+
}
|
|
2117
|
+
if (includeText) {
|
|
2118
|
+
if (context.text) {
|
|
2119
|
+
summary += ` Conteudo visivel: ${clipTextPreview(context.text, 360)}.`;
|
|
2120
|
+
}
|
|
2121
|
+
else {
|
|
2122
|
+
summary += " Nao consegui extrair o texto visivel desta pagina desta vez.";
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
if (includeTabs && context.tabs && context.tabs.length > 0) {
|
|
2126
|
+
const tabsPreview = context.tabs
|
|
2127
|
+
.slice(0, 4)
|
|
2128
|
+
.map((tab) => `${tab.current ? "[atual] " : ""}${tab.title || tab.url || "sem titulo"}`)
|
|
2129
|
+
.join(" | ");
|
|
2130
|
+
if (tabsPreview) {
|
|
2131
|
+
summary += ` Abas visiveis: ${tabsPreview}.`;
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
return summary;
|
|
2135
|
+
}
|
|
2136
|
+
async collectBrowserContext(preferredApp, includeText = false, includeTabs = false) {
|
|
2137
|
+
const app = await this.resolveBrowserContextApp(preferredApp);
|
|
2138
|
+
if (!app) {
|
|
2139
|
+
throw new Error("Nao encontrei Safari ou Google Chrome em foco para inspecionar agora.");
|
|
2140
|
+
}
|
|
2141
|
+
const page = includeText
|
|
2142
|
+
? await this.readCurrentBrowserPage(app)
|
|
2143
|
+
: await this.readCurrentBrowserMetadata(app).then((metadata) => ({ ...metadata, text: "" }));
|
|
2144
|
+
const tabs = includeTabs ? await this.listBrowserTabs(app) : undefined;
|
|
2145
|
+
const context = {
|
|
2146
|
+
app,
|
|
2147
|
+
title: page.title,
|
|
2148
|
+
url: page.url,
|
|
2149
|
+
text: page.text ? clipTextPreview(page.text, 1200) : undefined,
|
|
2150
|
+
tabs,
|
|
2151
|
+
summary: "",
|
|
2152
|
+
};
|
|
2153
|
+
context.summary = this.buildBrowserContextSummary(context, includeText, includeTabs);
|
|
2154
|
+
return context;
|
|
2155
|
+
}
|
|
2156
|
+
async readRunningApps(limit = 12) {
|
|
2157
|
+
try {
|
|
2158
|
+
const { stdout } = await this.runCommandCapture("osascript", [
|
|
2159
|
+
"-e",
|
|
2160
|
+
`
|
|
2161
|
+
tell application "System Events"
|
|
2162
|
+
set appNames to name of every application process whose background only is false
|
|
2163
|
+
end tell
|
|
2164
|
+
set AppleScript's text item delimiters to linefeed
|
|
2165
|
+
return appNames as text
|
|
2166
|
+
`,
|
|
2167
|
+
]);
|
|
2168
|
+
return Array.from(new Set(String(stdout || "")
|
|
2169
|
+
.split(/\r?\n/)
|
|
2170
|
+
.map((line) => line.trim())
|
|
2171
|
+
.filter(Boolean))).slice(0, limit);
|
|
2172
|
+
}
|
|
2173
|
+
catch {
|
|
2174
|
+
return [];
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
appMatchesProcessName(app, processName) {
|
|
2178
|
+
const normalizedApp = normalizeText(app || "").replace(/\s+/g, " ").trim();
|
|
2179
|
+
const normalizedProcess = normalizeText(processName || "").replace(/\s+/g, " ").trim();
|
|
2180
|
+
if (!normalizedApp || !normalizedProcess) {
|
|
2181
|
+
return false;
|
|
2182
|
+
}
|
|
2183
|
+
return normalizedProcess === normalizedApp
|
|
2184
|
+
|| normalizedProcess.includes(normalizedApp)
|
|
2185
|
+
|| normalizedApp.includes(normalizedProcess);
|
|
2186
|
+
}
|
|
2187
|
+
async readAppTopProcesses(limit = 5, preferredApp) {
|
|
2188
|
+
try {
|
|
2189
|
+
const { stdout } = await this.runCommandCapture("/bin/ps", ["-Ao", "pid=,pcpu=,rss=,comm=", "-r"]);
|
|
2190
|
+
const lines = stdout.trim().split(/\r?\n/).filter(Boolean);
|
|
2191
|
+
const processes = lines
|
|
2192
|
+
.map((line) => {
|
|
2193
|
+
const match = line.match(/^\s*(\d+)\s+([0-9.]+)\s+(\d+)\s+(.+)$/);
|
|
2194
|
+
if (!match)
|
|
2195
|
+
return null;
|
|
2196
|
+
const processName = match[4].trim().split("/").pop() || match[4].trim();
|
|
2197
|
+
return {
|
|
2198
|
+
name: processName,
|
|
2199
|
+
cpu_percent: roundMetric(Number(match[2]), 1),
|
|
2200
|
+
memory_bytes: Math.max(0, Number(match[3])) * 1024,
|
|
2201
|
+
};
|
|
2202
|
+
})
|
|
2203
|
+
.filter((item) => item !== null);
|
|
2204
|
+
const filtered = preferredApp
|
|
2205
|
+
? processes.filter((item) => this.appMatchesProcessName(preferredApp, item.name))
|
|
2206
|
+
: processes;
|
|
2207
|
+
return filtered
|
|
2208
|
+
.filter((item) => item.cpu_percent > 0 || (item.memory_bytes || 0) > 0)
|
|
2209
|
+
.slice(0, limit);
|
|
2210
|
+
}
|
|
2211
|
+
catch {
|
|
2212
|
+
return [];
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
buildAppStatusSummary(status) {
|
|
2216
|
+
const parts = [];
|
|
2217
|
+
if (status.target_app?.name) {
|
|
2218
|
+
if (!status.target_app.running) {
|
|
2219
|
+
parts.push(`${status.target_app.name} nao esta aberto agora`);
|
|
2220
|
+
}
|
|
2221
|
+
else if (status.target_app.frontmost) {
|
|
2222
|
+
parts.push(`${status.target_app.name} esta aberto e em foco agora`);
|
|
2223
|
+
}
|
|
2224
|
+
else {
|
|
2225
|
+
parts.push(`${status.target_app.name} esta aberto`);
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
else if (status.frontmost_app) {
|
|
2229
|
+
parts.push(`No foco agora esta ${status.frontmost_app}`);
|
|
2230
|
+
}
|
|
2231
|
+
if (status.running_apps && status.running_apps.length > 0) {
|
|
2232
|
+
const preview = status.running_apps.slice(0, 6).join(", ");
|
|
2233
|
+
parts.push(`apps abertos: ${preview}${status.running_apps.length > 6 ? ", ..." : ""}`);
|
|
2234
|
+
}
|
|
2235
|
+
let summary = parts.length > 0
|
|
2236
|
+
? `${parts[0]}.`
|
|
2237
|
+
: "Consegui ler o estado atual dos apps no seu Mac.";
|
|
2238
|
+
if (parts.length > 1) {
|
|
2239
|
+
summary += ` Também vejo ${parts.slice(1).join(". ")}.`;
|
|
2240
|
+
}
|
|
2241
|
+
if (status.top_processes && status.top_processes.length > 0) {
|
|
2242
|
+
const topPreview = status.top_processes
|
|
2243
|
+
.slice(0, 3)
|
|
2244
|
+
.map((item) => `${item.name} (${roundMetric(item.cpu_percent)}% CPU${item.memory_bytes ? `, ${formatBytesCompact(item.memory_bytes)}` : ""})`)
|
|
2245
|
+
.join(", ");
|
|
2246
|
+
if (topPreview) {
|
|
2247
|
+
summary += ` Maiores consumos agora: ${topPreview}.`;
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
return summary;
|
|
2251
|
+
}
|
|
2252
|
+
async collectAppStatus(preferredApp, includeFrontmost = true, includeRunningApps = true, includeTopProcesses = true) {
|
|
2253
|
+
const frontmostApp = includeFrontmost ? await this.getFrontmostAppName() : null;
|
|
2254
|
+
const runningApps = includeRunningApps ? await this.readRunningApps() : [];
|
|
2255
|
+
const normalizedRunningApps = Array.from(new Set(runningApps));
|
|
2256
|
+
const targetApp = preferredApp || undefined;
|
|
2257
|
+
const targetRunning = targetApp ? normalizedRunningApps.some((item) => normalizeText(item) === normalizeText(targetApp)) : undefined;
|
|
2258
|
+
const topProcesses = includeTopProcesses
|
|
2259
|
+
? await this.readAppTopProcesses(5, targetApp)
|
|
2260
|
+
: [];
|
|
2261
|
+
const status = {
|
|
2262
|
+
captured_at: new Date().toISOString(),
|
|
2263
|
+
hostname: os.hostname(),
|
|
2264
|
+
platform: process.platform,
|
|
2265
|
+
requested_app: targetApp,
|
|
2266
|
+
frontmost_app: frontmostApp || undefined,
|
|
2267
|
+
summary: "",
|
|
2268
|
+
};
|
|
2269
|
+
if (targetApp) {
|
|
2270
|
+
status.target_app = {
|
|
2271
|
+
name: targetApp,
|
|
2272
|
+
running: Boolean(targetRunning),
|
|
2273
|
+
frontmost: Boolean(frontmostApp && normalizeText(frontmostApp) === normalizeText(targetApp)),
|
|
2274
|
+
};
|
|
2275
|
+
}
|
|
2276
|
+
if (includeRunningApps && normalizedRunningApps.length > 0) {
|
|
2277
|
+
status.running_apps = normalizedRunningApps;
|
|
2278
|
+
}
|
|
2279
|
+
if (includeTopProcesses && topProcesses && topProcesses.length > 0) {
|
|
2280
|
+
status.top_processes = topProcesses;
|
|
2281
|
+
}
|
|
2282
|
+
status.summary = this.buildAppStatusSummary(status);
|
|
2283
|
+
return status;
|
|
2284
|
+
}
|
|
2285
|
+
async resolveFilesystemInspectPath(targetPath) {
|
|
2286
|
+
const expanded = expandUserPath(targetPath);
|
|
2287
|
+
try {
|
|
2288
|
+
await stat(expanded);
|
|
2289
|
+
return expanded;
|
|
2290
|
+
}
|
|
2291
|
+
catch {
|
|
2292
|
+
// Continue below.
|
|
2293
|
+
}
|
|
2294
|
+
if (path.extname(expanded)) {
|
|
2295
|
+
return this.resolveReadableFilePath(targetPath);
|
|
2296
|
+
}
|
|
2297
|
+
return expanded;
|
|
2298
|
+
}
|
|
2299
|
+
async readDirectoryUsageBytes(targetPath) {
|
|
2300
|
+
try {
|
|
2301
|
+
const { stdout } = await this.runCommandCapture("/usr/bin/du", ["-sk", targetPath]);
|
|
2302
|
+
const match = String(stdout || "").match(/^\s*(\d+)/);
|
|
2303
|
+
if (!match) {
|
|
2304
|
+
return 0;
|
|
2305
|
+
}
|
|
2306
|
+
return Number(match[1]) * 1024;
|
|
2307
|
+
}
|
|
2308
|
+
catch {
|
|
2309
|
+
return 0;
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
async inspectFilesystemPath(targetPath, includeChildren = true, includePreview = false, limit = 8) {
|
|
2313
|
+
const resolved = await this.resolveFilesystemInspectPath(targetPath);
|
|
2314
|
+
const entryStat = await stat(resolved);
|
|
2315
|
+
const itemName = path.basename(resolved) || resolved;
|
|
2316
|
+
if (entryStat.isDirectory()) {
|
|
2317
|
+
const entries = await readdir(resolved, { withFileTypes: true });
|
|
2318
|
+
const slicedEntries = entries
|
|
2319
|
+
.sort((left, right) => {
|
|
2320
|
+
if (left.isDirectory() !== right.isDirectory()) {
|
|
2321
|
+
return left.isDirectory() ? -1 : 1;
|
|
2322
|
+
}
|
|
2323
|
+
return left.name.localeCompare(right.name);
|
|
2324
|
+
})
|
|
2325
|
+
.slice(0, Math.max(1, Math.min(limit, 20)));
|
|
2326
|
+
const children = includeChildren
|
|
2327
|
+
? await Promise.all(slicedEntries.map(async (entry) => {
|
|
2328
|
+
const childPath = path.join(resolved, entry.name);
|
|
2329
|
+
try {
|
|
2330
|
+
const childStat = await stat(childPath);
|
|
2331
|
+
return {
|
|
2332
|
+
name: entry.name,
|
|
2333
|
+
kind: entry.isDirectory() ? "directory" : "file",
|
|
2334
|
+
size_bytes: entry.isDirectory() ? undefined : childStat.size,
|
|
2335
|
+
};
|
|
2336
|
+
}
|
|
2337
|
+
catch {
|
|
2338
|
+
return {
|
|
2339
|
+
name: entry.name,
|
|
2340
|
+
kind: entry.isDirectory() ? "directory" : "file",
|
|
2341
|
+
};
|
|
2342
|
+
}
|
|
2343
|
+
}))
|
|
2344
|
+
: undefined;
|
|
2345
|
+
const totalSize = await this.readDirectoryUsageBytes(resolved);
|
|
2346
|
+
const snapshot = {
|
|
2347
|
+
captured_at: new Date().toISOString(),
|
|
2348
|
+
path: targetPath,
|
|
2349
|
+
resolved_path: resolved,
|
|
2350
|
+
kind: "directory",
|
|
2351
|
+
name: itemName,
|
|
2352
|
+
size_bytes: totalSize,
|
|
2353
|
+
modified_at: entryStat.mtime.toISOString(),
|
|
2354
|
+
item_count: entries.length,
|
|
2355
|
+
children,
|
|
2356
|
+
summary: "",
|
|
2357
|
+
};
|
|
2358
|
+
const childPreview = children && children.length > 0
|
|
2359
|
+
? children
|
|
2360
|
+
.slice(0, 5)
|
|
2361
|
+
.map((item) => `${item.name}${item.kind === "directory" ? "/" : ""}`)
|
|
2362
|
+
.join(", ")
|
|
2363
|
+
: "";
|
|
2364
|
+
snapshot.summary = `A pasta ${targetPath} tem ${entries.length} item${entries.length === 1 ? "" : "s"} e ocupa ${formatBytesCompact(totalSize)}.${childPreview ? ` Itens visiveis agora: ${childPreview}.` : ""}`;
|
|
2365
|
+
return snapshot;
|
|
2366
|
+
}
|
|
2367
|
+
const preview = includePreview ? await this.readLocalFile(resolved, 1200) : undefined;
|
|
2368
|
+
const snapshot = {
|
|
2369
|
+
captured_at: new Date().toISOString(),
|
|
2370
|
+
path: targetPath,
|
|
2371
|
+
resolved_path: resolved,
|
|
2372
|
+
kind: "file",
|
|
2373
|
+
name: itemName,
|
|
2374
|
+
size_bytes: entryStat.size,
|
|
2375
|
+
modified_at: entryStat.mtime.toISOString(),
|
|
2376
|
+
preview: preview || undefined,
|
|
2377
|
+
summary: "",
|
|
2378
|
+
};
|
|
2379
|
+
snapshot.summary = `O arquivo ${targetPath} pesa ${formatBytesCompact(entryStat.size)} e foi modificado em ${entryStat.mtime.toISOString()}.${preview ? ` Pre-visualizacao: ${clipTextPreview(preview, 320)}.` : ""}`;
|
|
2380
|
+
return snapshot;
|
|
2381
|
+
}
|
|
1864
2382
|
async captureBrowserPageState(app) {
|
|
1865
2383
|
if (app !== "Safari") {
|
|
1866
2384
|
return null;
|
|
@@ -4600,6 +5118,233 @@ if let output = String(data: data, encoding: .utf8) {
|
|
|
4600
5118
|
extensionsLabel,
|
|
4601
5119
|
};
|
|
4602
5120
|
}
|
|
5121
|
+
snapshotCpuTimes() {
|
|
5122
|
+
const cpus = os.cpus();
|
|
5123
|
+
let idle = 0;
|
|
5124
|
+
let total = 0;
|
|
5125
|
+
for (const cpu of cpus) {
|
|
5126
|
+
idle += cpu.times.idle;
|
|
5127
|
+
total += cpu.times.user + cpu.times.nice + cpu.times.sys + cpu.times.irq + cpu.times.idle;
|
|
5128
|
+
}
|
|
5129
|
+
return {
|
|
5130
|
+
idle,
|
|
5131
|
+
total,
|
|
5132
|
+
model: cpus[0]?.model || "Apple Silicon",
|
|
5133
|
+
logicalCores: cpus.length || 0,
|
|
5134
|
+
};
|
|
5135
|
+
}
|
|
5136
|
+
async sampleCpuStatus() {
|
|
5137
|
+
const start = this.snapshotCpuTimes();
|
|
5138
|
+
await delay(320);
|
|
5139
|
+
const end = this.snapshotCpuTimes();
|
|
5140
|
+
const totalDelta = Math.max(1, end.total - start.total);
|
|
5141
|
+
const idleDelta = Math.max(0, end.idle - start.idle);
|
|
5142
|
+
const idlePercent = roundMetric((idleDelta / totalDelta) * 100, 1);
|
|
5143
|
+
const usagePercent = roundMetric(Math.max(0, 100 - idlePercent), 1);
|
|
5144
|
+
const [load1m, load5m, load15m] = os.loadavg();
|
|
5145
|
+
return {
|
|
5146
|
+
usage_percent: usagePercent,
|
|
5147
|
+
idle_percent: idlePercent,
|
|
5148
|
+
logical_cores: end.logicalCores,
|
|
5149
|
+
model: end.model,
|
|
5150
|
+
load_average_1m: roundMetric(load1m, 2),
|
|
5151
|
+
load_average_5m: roundMetric(load5m, 2),
|
|
5152
|
+
load_average_15m: roundMetric(load15m, 2),
|
|
5153
|
+
};
|
|
5154
|
+
}
|
|
5155
|
+
async readMemoryStatus() {
|
|
5156
|
+
const totalBytes = os.totalmem();
|
|
5157
|
+
const freeBytes = os.freemem();
|
|
5158
|
+
const usedBytes = Math.max(0, totalBytes - freeBytes);
|
|
5159
|
+
let compressedBytes = 0;
|
|
5160
|
+
let swapUsedBytes = 0;
|
|
5161
|
+
try {
|
|
5162
|
+
const { stdout } = await this.runCommandCapture("/usr/bin/vm_stat", []);
|
|
5163
|
+
const pageSizeMatch = stdout.match(/page size of (\d+) bytes/i);
|
|
5164
|
+
const pageSize = pageSizeMatch ? Number(pageSizeMatch[1]) : 16384;
|
|
5165
|
+
const compressedMatch = stdout.match(/Pages occupied by compressor:\s+([0-9.]+)/i);
|
|
5166
|
+
if (compressedMatch) {
|
|
5167
|
+
compressedBytes = Math.round(Number(compressedMatch[1]) * pageSize);
|
|
5168
|
+
}
|
|
5169
|
+
}
|
|
5170
|
+
catch {
|
|
5171
|
+
compressedBytes = 0;
|
|
5172
|
+
}
|
|
5173
|
+
try {
|
|
5174
|
+
const { stdout } = await this.runCommandCapture("/usr/sbin/sysctl", ["vm.swapusage"]);
|
|
5175
|
+
const usedMatch = stdout.match(/used = ([0-9.]+)([BKMGTP]+)/i);
|
|
5176
|
+
if (usedMatch) {
|
|
5177
|
+
swapUsedBytes = parseScaledBytes(usedMatch[1], usedMatch[2]);
|
|
5178
|
+
}
|
|
5179
|
+
}
|
|
5180
|
+
catch {
|
|
5181
|
+
swapUsedBytes = 0;
|
|
5182
|
+
}
|
|
5183
|
+
const usedPercent = totalBytes > 0 ? roundMetric((usedBytes / totalBytes) * 100, 1) : 0;
|
|
5184
|
+
const pressure = (usedPercent >= 90 || swapUsedBytes >= 1.5 * 1024 ** 3)
|
|
5185
|
+
? "high"
|
|
5186
|
+
: (usedPercent >= 80 || swapUsedBytes >= 512 * 1024 ** 2 || compressedBytes >= 1024 ** 3)
|
|
5187
|
+
? "attention"
|
|
5188
|
+
: "normal";
|
|
5189
|
+
return {
|
|
5190
|
+
total_bytes: totalBytes,
|
|
5191
|
+
used_bytes: usedBytes,
|
|
5192
|
+
free_bytes: freeBytes,
|
|
5193
|
+
used_percent: usedPercent,
|
|
5194
|
+
pressure,
|
|
5195
|
+
swap_used_bytes: swapUsedBytes || undefined,
|
|
5196
|
+
compressed_bytes: compressedBytes || undefined,
|
|
5197
|
+
};
|
|
5198
|
+
}
|
|
5199
|
+
async readDiskStatus() {
|
|
5200
|
+
const { stdout } = await this.runCommandCapture("/bin/df", ["-k", "/"]);
|
|
5201
|
+
const lines = stdout.trim().split(/\r?\n/).filter(Boolean);
|
|
5202
|
+
if (lines.length < 2) {
|
|
5203
|
+
return undefined;
|
|
5204
|
+
}
|
|
5205
|
+
const parts = lines[1].trim().split(/\s+/);
|
|
5206
|
+
if (parts.length < 6) {
|
|
5207
|
+
return undefined;
|
|
5208
|
+
}
|
|
5209
|
+
const totalBytes = Number(parts[1]) * 1024;
|
|
5210
|
+
const usedBytes = Number(parts[2]) * 1024;
|
|
5211
|
+
const availableBytes = Number(parts[3]) * 1024;
|
|
5212
|
+
const usedPercent = roundMetric(Number((parts[4] || "").replace("%", "")), 1);
|
|
5213
|
+
return {
|
|
5214
|
+
mount_path: parts[5] || "/",
|
|
5215
|
+
total_bytes: totalBytes,
|
|
5216
|
+
used_bytes: usedBytes,
|
|
5217
|
+
available_bytes: availableBytes,
|
|
5218
|
+
used_percent: usedPercent,
|
|
5219
|
+
available_gb: roundMetric(availableBytes / (1024 ** 3), 1),
|
|
5220
|
+
};
|
|
5221
|
+
}
|
|
5222
|
+
async readBatteryStatus() {
|
|
5223
|
+
try {
|
|
5224
|
+
const { stdout } = await this.runCommandCapture("/usr/bin/pmset", ["-g", "batt"]);
|
|
5225
|
+
const percentageMatch = stdout.match(/(\d+)%/);
|
|
5226
|
+
if (!percentageMatch) {
|
|
5227
|
+
return undefined;
|
|
5228
|
+
}
|
|
5229
|
+
const powerSourceMatch = stdout.match(/Now drawing from '([^']+)'/i);
|
|
5230
|
+
const powerSource = powerSourceMatch?.[1]?.trim() || "Unknown";
|
|
5231
|
+
const normalized = stdout.toLowerCase();
|
|
5232
|
+
const charging = normalized.includes("charging") || normalized.includes("charged") || powerSource.toLowerCase().includes("ac");
|
|
5233
|
+
return {
|
|
5234
|
+
percentage: Math.max(0, Math.min(100, Number(percentageMatch[1]))),
|
|
5235
|
+
charging,
|
|
5236
|
+
power_source: powerSource,
|
|
5237
|
+
};
|
|
5238
|
+
}
|
|
5239
|
+
catch {
|
|
5240
|
+
return undefined;
|
|
5241
|
+
}
|
|
5242
|
+
}
|
|
5243
|
+
async readTopProcesses(limit = 4) {
|
|
5244
|
+
try {
|
|
5245
|
+
const { stdout } = await this.runCommandCapture("/bin/ps", ["-Ao", "pcpu,comm", "-r"]);
|
|
5246
|
+
const lines = stdout.trim().split(/\r?\n/).slice(1);
|
|
5247
|
+
return lines
|
|
5248
|
+
.map((line) => line.trim())
|
|
5249
|
+
.filter(Boolean)
|
|
5250
|
+
.slice(0, limit)
|
|
5251
|
+
.map((line) => {
|
|
5252
|
+
const match = line.match(/^([0-9.]+)\s+(.+)$/);
|
|
5253
|
+
if (!match)
|
|
5254
|
+
return null;
|
|
5255
|
+
const cpuPercent = roundMetric(Number(match[1]), 1);
|
|
5256
|
+
const command = match[2].trim().split("/").pop() || match[2].trim();
|
|
5257
|
+
return {
|
|
5258
|
+
name: command,
|
|
5259
|
+
cpu_percent: cpuPercent,
|
|
5260
|
+
};
|
|
5261
|
+
})
|
|
5262
|
+
.filter((item) => item !== null && item.cpu_percent > 0);
|
|
5263
|
+
}
|
|
5264
|
+
catch {
|
|
5265
|
+
return [];
|
|
5266
|
+
}
|
|
5267
|
+
}
|
|
5268
|
+
buildSystemStatusSummary(status) {
|
|
5269
|
+
const parts = [];
|
|
5270
|
+
const warnings = [];
|
|
5271
|
+
if (status.cpu) {
|
|
5272
|
+
parts.push(`CPU em ${roundMetric(status.cpu.usage_percent)}%`);
|
|
5273
|
+
if (status.cpu.usage_percent >= 85) {
|
|
5274
|
+
warnings.push(`CPU em ${roundMetric(status.cpu.usage_percent)}%`);
|
|
5275
|
+
}
|
|
5276
|
+
}
|
|
5277
|
+
if (status.memory) {
|
|
5278
|
+
parts.push(`memoria em ${roundMetric(status.memory.used_percent)}%`);
|
|
5279
|
+
if (status.memory.pressure === "high") {
|
|
5280
|
+
warnings.push(`memoria pressionada (${roundMetric(status.memory.used_percent)}% e swap ativo)`);
|
|
5281
|
+
}
|
|
5282
|
+
else if (status.memory.pressure === "attention") {
|
|
5283
|
+
warnings.push(`memoria em ${roundMetric(status.memory.used_percent)}%`);
|
|
5284
|
+
}
|
|
5285
|
+
}
|
|
5286
|
+
if (status.disk) {
|
|
5287
|
+
parts.push(`${formatBytesCompact(status.disk.available_bytes)} livres no disco`);
|
|
5288
|
+
if (status.disk.used_percent >= 90 || status.disk.available_bytes <= 15 * 1024 ** 3) {
|
|
5289
|
+
warnings.push(`pouco espaco livre (${formatBytesCompact(status.disk.available_bytes)})`);
|
|
5290
|
+
}
|
|
5291
|
+
}
|
|
5292
|
+
if (status.battery) {
|
|
5293
|
+
parts.push(`bateria em ${status.battery.percentage}%${status.battery.charging ? " carregando" : ""}`);
|
|
5294
|
+
if (!status.battery.charging && status.battery.percentage <= 20) {
|
|
5295
|
+
warnings.push(`bateria em ${status.battery.percentage}%`);
|
|
5296
|
+
}
|
|
5297
|
+
}
|
|
5298
|
+
let summary = warnings.length > 0
|
|
5299
|
+
? `Seu Mac esta operando, mas merece atencao em ${warnings.join(" e ")}.`
|
|
5300
|
+
: "No geral, seu Mac esta de boa.";
|
|
5301
|
+
if (parts.length > 0) {
|
|
5302
|
+
summary += ` Agora vejo ${parts.join(", ")}.`;
|
|
5303
|
+
}
|
|
5304
|
+
if (status.top_processes && status.top_processes.length > 0) {
|
|
5305
|
+
const topProcesses = status.top_processes
|
|
5306
|
+
.slice(0, 3)
|
|
5307
|
+
.map((item) => `${item.name} (${roundMetric(item.cpu_percent)}%)`)
|
|
5308
|
+
.join(", ");
|
|
5309
|
+
if (topProcesses) {
|
|
5310
|
+
summary += ` Maiores consumos agora: ${topProcesses}.`;
|
|
5311
|
+
}
|
|
5312
|
+
}
|
|
5313
|
+
return summary;
|
|
5314
|
+
}
|
|
5315
|
+
async collectSystemStatus(sections, includeTopProcesses = true) {
|
|
5316
|
+
const requestedSections = sections && sections.length > 0
|
|
5317
|
+
? sections
|
|
5318
|
+
: ["cpu", "memory", "disk", "battery"];
|
|
5319
|
+
const uniqueSections = Array.from(new Set(requestedSections));
|
|
5320
|
+
const status = {
|
|
5321
|
+
captured_at: new Date().toISOString(),
|
|
5322
|
+
hostname: os.hostname(),
|
|
5323
|
+
platform: process.platform,
|
|
5324
|
+
requested_sections: uniqueSections,
|
|
5325
|
+
summary: "",
|
|
5326
|
+
};
|
|
5327
|
+
if (uniqueSections.includes("cpu")) {
|
|
5328
|
+
status.cpu = await this.sampleCpuStatus();
|
|
5329
|
+
}
|
|
5330
|
+
if (uniqueSections.includes("memory")) {
|
|
5331
|
+
status.memory = await this.readMemoryStatus();
|
|
5332
|
+
}
|
|
5333
|
+
if (uniqueSections.includes("disk")) {
|
|
5334
|
+
status.disk = await this.readDiskStatus();
|
|
5335
|
+
}
|
|
5336
|
+
if (uniqueSections.includes("battery")) {
|
|
5337
|
+
status.battery = await this.readBatteryStatus();
|
|
5338
|
+
}
|
|
5339
|
+
if (includeTopProcesses && (uniqueSections.includes("cpu") || uniqueSections.includes("memory"))) {
|
|
5340
|
+
const topProcesses = await this.readTopProcesses();
|
|
5341
|
+
if (topProcesses && topProcesses.length > 0) {
|
|
5342
|
+
status.top_processes = topProcesses;
|
|
5343
|
+
}
|
|
5344
|
+
}
|
|
5345
|
+
status.summary = this.buildSystemStatusSummary(status);
|
|
5346
|
+
return status;
|
|
5347
|
+
}
|
|
4603
5348
|
async runShellCommand(command, cwd) {
|
|
4604
5349
|
if (!isSafeShellCommand(command)) {
|
|
4605
5350
|
throw new Error("Nenhum comando shell foi informado para execucao local.");
|
|
@@ -4653,6 +5398,15 @@ if let output = String(data: data, encoding: .utf8) {
|
|
|
4653
5398
|
if (action.type === "read_frontmost_page") {
|
|
4654
5399
|
return `Pagina ativa lida em ${action.app || "Safari"}`;
|
|
4655
5400
|
}
|
|
5401
|
+
if (action.type === "browser_context") {
|
|
5402
|
+
return `Contexto do navegador coletado${action.app ? ` em ${action.app}` : ""}`;
|
|
5403
|
+
}
|
|
5404
|
+
if (action.type === "app_status") {
|
|
5405
|
+
return `Status de apps coletado${action.app ? ` para ${action.app}` : ""}`;
|
|
5406
|
+
}
|
|
5407
|
+
if (action.type === "filesystem_inspect") {
|
|
5408
|
+
return `Inspecao local concluida em ${action.path}`;
|
|
5409
|
+
}
|
|
4656
5410
|
if (action.type === "read_file") {
|
|
4657
5411
|
return `${action.path} foi lido no macOS`;
|
|
4658
5412
|
}
|
|
@@ -4662,6 +5416,9 @@ if let output = String(data: data, encoding: .utf8) {
|
|
|
4662
5416
|
if (action.type === "count_files") {
|
|
4663
5417
|
return `Arquivos contados em ${action.path}`;
|
|
4664
5418
|
}
|
|
5419
|
+
if (action.type === "system_status") {
|
|
5420
|
+
return "Status do macOS coletado";
|
|
5421
|
+
}
|
|
4665
5422
|
if (action.type === "run_shell") {
|
|
4666
5423
|
return `Comando ${action.command} executado no macOS`;
|
|
4667
5424
|
}
|
package/dist/types.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const BRIDGE_CONFIG_VERSION = 1;
|
|
2
|
-
export const BRIDGE_VERSION = "0.8.
|
|
2
|
+
export const BRIDGE_VERSION = "0.8.5";
|
|
3
3
|
export const BRIDGE_PACKAGE_NAME = "@leg3ndy/otto-bridge";
|
|
4
4
|
export const DEFAULT_API_BASE_URL = "http://localhost:8000";
|
|
5
5
|
export const DEFAULT_POLL_INTERVAL_MS = 3000;
|