@floless/app 0.6.1 → 0.6.3
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/floless-server.cjs +1373 -1364
- package/launch.mjs +23 -3
- package/package.json +1 -1
package/dist/floless-server.cjs
CHANGED
|
@@ -50826,11 +50826,11 @@ var import_node_child_process8 = require("node:child_process");
|
|
|
50826
50826
|
var import_node_readline2 = require("node:readline");
|
|
50827
50827
|
|
|
50828
50828
|
// index.ts
|
|
50829
|
-
var
|
|
50830
|
-
var
|
|
50829
|
+
var import_node_url3 = require("node:url");
|
|
50830
|
+
var import_node_path17 = require("node:path");
|
|
50831
50831
|
var import_node_os11 = require("node:os");
|
|
50832
|
-
var
|
|
50833
|
-
var
|
|
50832
|
+
var import_node_fs18 = require("node:fs");
|
|
50833
|
+
var import_node_child_process6 = require("node:child_process");
|
|
50834
50834
|
|
|
50835
50835
|
// log.mjs
|
|
50836
50836
|
var import_node_path = require("node:path");
|
|
@@ -51148,7 +51148,7 @@ function nodeExe() {
|
|
|
51148
51148
|
}
|
|
51149
51149
|
if (!isSea3) return _nodeExe = process.execPath;
|
|
51150
51150
|
try {
|
|
51151
|
-
const out = (0, import_node_child_process.execFileSync)(process.platform === "win32" ? "where" : "which", ["node"], { encoding: "utf8" });
|
|
51151
|
+
const out = (0, import_node_child_process.execFileSync)(process.platform === "win32" ? "where" : "which", ["node"], { encoding: "utf8", windowsHide: true, timeout: 5e3 });
|
|
51152
51152
|
_nodeExe = out.split(/\r?\n/).map((s) => s.trim()).find(Boolean) ?? "node";
|
|
51153
51153
|
} catch {
|
|
51154
51154
|
_nodeExe = "node";
|
|
@@ -51171,7 +51171,7 @@ function resolveInvoker() {
|
|
|
51171
51171
|
return { head: [nodeExe(), (0, import_node_path3.join)(envDir, "scripts/bin/aware.js")], shell: false, pkgDir: envDir };
|
|
51172
51172
|
}
|
|
51173
51173
|
try {
|
|
51174
|
-
const root = (0, import_node_child_process.execFileSync)("npm", ["root", "-g"], { encoding: "utf8", shell: process.platform === "win32" }).trim();
|
|
51174
|
+
const root = (0, import_node_child_process.execFileSync)("npm", ["root", "-g"], { encoding: "utf8", shell: process.platform === "win32", windowsHide: true, timeout: 1e4 }).trim();
|
|
51175
51175
|
const pkgDir = (0, import_node_path3.join)(root, "@aware-aeco", "cli");
|
|
51176
51176
|
const bin = (0, import_node_path3.join)(pkgDir, "scripts/bin/aware.js");
|
|
51177
51177
|
if ((0, import_node_fs4.existsSync)(bin)) return { head: [nodeExe(), bin], shell: false, pkgDir };
|
|
@@ -51345,7 +51345,7 @@ var runChain = Promise.resolve();
|
|
|
51345
51345
|
function killTree(child) {
|
|
51346
51346
|
if (process.platform === "win32" && child.pid) {
|
|
51347
51347
|
try {
|
|
51348
|
-
(0, import_node_child_process.execFileSync)("taskkill", ["/pid", String(child.pid), "/T", "/F"], { stdio: "ignore" });
|
|
51348
|
+
(0, import_node_child_process.execFileSync)("taskkill", ["/pid", String(child.pid), "/T", "/F"], { stdio: "ignore", windowsHide: true, timeout: 5e3 });
|
|
51349
51349
|
return;
|
|
51350
51350
|
} catch {
|
|
51351
51351
|
}
|
|
@@ -52161,8 +52161,12 @@ function browserCommand(url, platform = process.platform) {
|
|
|
52161
52161
|
if (platform === "win32") {
|
|
52162
52162
|
return {
|
|
52163
52163
|
cmd: "cmd",
|
|
52164
|
+
// NO `detached`: Node #21825 makes it defeat `windowsHide` for cmd.exe (a CUI child),
|
|
52165
|
+
// flashing a console window on sign-in. windowsHide alone hides it; `start` still hands
|
|
52166
|
+
// the URL to the browser via ShellExecute, which outlives this process. windowsVerbatimArguments
|
|
52167
|
+
// keeps `&` literal inside the quoted url (the desktop-session footgun documented above).
|
|
52164
52168
|
args: ["/c", "start", '""', `"${url}"`],
|
|
52165
|
-
opts: { windowsHide: true,
|
|
52169
|
+
opts: { windowsHide: true, windowsVerbatimArguments: true }
|
|
52166
52170
|
};
|
|
52167
52171
|
}
|
|
52168
52172
|
return { cmd: platform === "darwin" ? "open" : "xdg-open", args: [url], opts: { detached: true } };
|
|
@@ -52254,7 +52258,7 @@ function appVersion() {
|
|
|
52254
52258
|
return resolveVersion({
|
|
52255
52259
|
isSea: isSea2(),
|
|
52256
52260
|
sqVersionXml: readSqVersionXml(),
|
|
52257
|
-
define: true ? "0.6.
|
|
52261
|
+
define: true ? "0.6.3" : void 0,
|
|
52258
52262
|
pkgVersion: readPkgVersion()
|
|
52259
52263
|
});
|
|
52260
52264
|
}
|
|
@@ -52264,7 +52268,7 @@ function resolveChannel(s) {
|
|
|
52264
52268
|
return "dev";
|
|
52265
52269
|
}
|
|
52266
52270
|
function appChannel() {
|
|
52267
|
-
return resolveChannel({ isSea: isSea2(), define: true ? "0.6.
|
|
52271
|
+
return resolveChannel({ isSea: isSea2(), define: true ? "0.6.3" : void 0 });
|
|
52268
52272
|
}
|
|
52269
52273
|
|
|
52270
52274
|
// bake.ts
|
|
@@ -53733,379 +53737,781 @@ async function applyUpdate(check, opts) {
|
|
|
53733
53737
|
return { applied: true, message: `updating to v${check.targetVersion}\u2026 the app will relaunch` };
|
|
53734
53738
|
}
|
|
53735
53739
|
|
|
53736
|
-
//
|
|
53737
|
-
var
|
|
53738
|
-
var
|
|
53739
|
-
var
|
|
53740
|
-
var
|
|
53741
|
-
|
|
53742
|
-
|
|
53743
|
-
|
|
53744
|
-
|
|
53745
|
-
|
|
53746
|
-
}
|
|
53747
|
-
|
|
53748
|
-
|
|
53749
|
-
}
|
|
53750
|
-
function versionGt2(a, b) {
|
|
53751
|
-
const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
|
|
53752
|
-
const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
|
|
53753
|
-
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
53754
|
-
const d = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
53755
|
-
if (d !== 0) return d > 0;
|
|
53756
|
-
}
|
|
53757
|
-
return false;
|
|
53758
|
-
}
|
|
53759
|
-
function decideAwareUpdate(cur, latest) {
|
|
53760
|
-
const base = { supported: true, currentVersion: cur };
|
|
53761
|
-
if (!latest) return { ...base, updateAvailable: false, reason: "could not read the npm registry latest" };
|
|
53762
|
-
if (!cur) return { ...base, updateAvailable: false, targetVersion: latest, reason: "AWARE not installed yet" };
|
|
53763
|
-
if (versionGt2(latest, cur)) return { ...base, updateAvailable: true, targetVersion: latest };
|
|
53764
|
-
return { ...base, updateAvailable: false, targetVersion: latest, reason: `already up to date (v${cur})` };
|
|
53765
|
-
}
|
|
53766
|
-
async function fetchAwareLatest() {
|
|
53740
|
+
// launch.mjs
|
|
53741
|
+
var import_node_child_process5 = require("node:child_process");
|
|
53742
|
+
var import_node_path13 = require("node:path");
|
|
53743
|
+
var import_node_url = require("node:url");
|
|
53744
|
+
var import_node_fs15 = require("node:fs");
|
|
53745
|
+
var import_node_http = __toESM(require("node:http"), 1);
|
|
53746
|
+
var import_node_readline = require("node:readline");
|
|
53747
|
+
var __dirname2 = (0, import_node_path13.dirname)((0, import_node_url.fileURLToPath)(__import_meta_url));
|
|
53748
|
+
var PORT = Number(process.env.PORT ?? 4317);
|
|
53749
|
+
var HEALTH_URL = `http://127.0.0.1:${PORT}/api/health`;
|
|
53750
|
+
var BROWSER_URL = `http://floless.localhost:${PORT}`;
|
|
53751
|
+
var isWin2 = process.platform === "win32";
|
|
53752
|
+
var log = (m) => {
|
|
53767
53753
|
try {
|
|
53768
|
-
|
|
53769
|
-
|
|
53770
|
-
const body = await res.json();
|
|
53771
|
-
return typeof body.version === "string" ? body.version : null;
|
|
53754
|
+
process.stdout.write(`floless: ${m}
|
|
53755
|
+
`);
|
|
53772
53756
|
} catch {
|
|
53773
|
-
return null;
|
|
53774
53757
|
}
|
|
53758
|
+
};
|
|
53759
|
+
function ping() {
|
|
53760
|
+
return new Promise((resolve4) => {
|
|
53761
|
+
const req = import_node_http.default.get(HEALTH_URL, { timeout: 1500 }, (res) => {
|
|
53762
|
+
res.resume();
|
|
53763
|
+
resolve4(res.statusCode === 200);
|
|
53764
|
+
});
|
|
53765
|
+
req.on("error", () => resolve4(false));
|
|
53766
|
+
req.on("timeout", () => {
|
|
53767
|
+
req.destroy();
|
|
53768
|
+
resolve4(false);
|
|
53769
|
+
});
|
|
53770
|
+
});
|
|
53775
53771
|
}
|
|
53776
|
-
|
|
53777
|
-
|
|
53778
|
-
|
|
53779
|
-
|
|
53772
|
+
function probeVersion() {
|
|
53773
|
+
return new Promise((resolve4) => {
|
|
53774
|
+
const req = import_node_http.default.get(HEALTH_URL, { timeout: 1500 }, (res) => {
|
|
53775
|
+
let body = "";
|
|
53776
|
+
res.on("data", (c) => {
|
|
53777
|
+
body += c;
|
|
53778
|
+
});
|
|
53779
|
+
res.on("end", () => {
|
|
53780
|
+
try {
|
|
53781
|
+
resolve4(JSON.parse(body).appVersion || null);
|
|
53782
|
+
} catch {
|
|
53783
|
+
resolve4(null);
|
|
53784
|
+
}
|
|
53785
|
+
});
|
|
53786
|
+
});
|
|
53787
|
+
req.on("error", () => resolve4(null));
|
|
53788
|
+
req.on("timeout", () => {
|
|
53789
|
+
req.destroy();
|
|
53790
|
+
resolve4(null);
|
|
53791
|
+
});
|
|
53792
|
+
});
|
|
53780
53793
|
}
|
|
53781
|
-
|
|
53782
|
-
|
|
53783
|
-
|
|
53784
|
-
|
|
53785
|
-
|
|
53786
|
-
return
|
|
53787
|
-
} catch (err) {
|
|
53788
|
-
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
53794
|
+
function cmpVersion(a, b) {
|
|
53795
|
+
const pa = String(a ?? "").split(".").map((n) => parseInt(n, 10) || 0);
|
|
53796
|
+
const pb = String(b ?? "").split(".").map((n) => parseInt(n, 10) || 0);
|
|
53797
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
53798
|
+
const d = (pa[i] || 0) - (pb[i] || 0);
|
|
53799
|
+
if (d !== 0) return d > 0 ? 1 : -1;
|
|
53789
53800
|
}
|
|
53801
|
+
return 0;
|
|
53790
53802
|
}
|
|
53791
|
-
function
|
|
53792
|
-
|
|
53803
|
+
function shouldTakeOver(runningVersion, selfVersion) {
|
|
53804
|
+
if (!runningVersion || !selfVersion) return false;
|
|
53805
|
+
return cmpVersion(selfVersion, runningVersion) > 0;
|
|
53793
53806
|
}
|
|
53794
|
-
|
|
53795
|
-
|
|
53796
|
-
|
|
53797
|
-
|
|
53798
|
-
|
|
53807
|
+
var _selfVersion = null;
|
|
53808
|
+
async function waitHealthy(timeoutMs = 3e4) {
|
|
53809
|
+
const deadline = Date.now() + timeoutMs;
|
|
53810
|
+
while (Date.now() < deadline) {
|
|
53811
|
+
if (await ping()) return true;
|
|
53812
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
53813
|
+
}
|
|
53814
|
+
return false;
|
|
53799
53815
|
}
|
|
53800
|
-
|
|
53801
|
-
|
|
53802
|
-
|
|
53803
|
-
|
|
53804
|
-
|
|
53805
|
-
|
|
53806
|
-
var import_yaml5 = __toESM(require_dist6(), 1);
|
|
53807
|
-
|
|
53808
|
-
// build/ship-skills.mjs
|
|
53809
|
-
var PRODUCT_SKILLS = [
|
|
53810
|
-
"floless-app-bridge",
|
|
53811
|
-
// drive the floless.app CLI / desktop bridge from the user's AI
|
|
53812
|
-
"floless-app-onboarding",
|
|
53813
|
-
// guided, re-runnable tour of AWARE + floless.app for new users
|
|
53814
|
-
"floless-app-routines",
|
|
53815
|
-
// author event-driven ("on trigger") routines
|
|
53816
|
-
"floless-app-workflows"
|
|
53817
|
-
// author/run .flo workflows
|
|
53818
|
-
];
|
|
53819
|
-
function selectShippedSkillNames(names) {
|
|
53820
|
-
const present = new Set(names);
|
|
53821
|
-
return PRODUCT_SKILLS.filter((name) => present.has(name));
|
|
53816
|
+
function resolveServerStart() {
|
|
53817
|
+
const packaged = /flolessapp\.exe$/i.test(process.execPath);
|
|
53818
|
+
if (packaged) return { cmd: process.execPath, args: ["--serve"], shell: false };
|
|
53819
|
+
const bundle = (0, import_node_path13.join)(__dirname2, "dist", "floless-server.cjs");
|
|
53820
|
+
if ((0, import_node_fs15.existsSync)(bundle)) return { cmd: process.execPath, args: [bundle, "--serve"], shell: false };
|
|
53821
|
+
return { cmd: "npm", args: ["run", "start"], shell: isWin2 };
|
|
53822
53822
|
}
|
|
53823
|
-
|
|
53824
|
-
|
|
53825
|
-
|
|
53826
|
-
|
|
53827
|
-
const
|
|
53828
|
-
|
|
53829
|
-
|
|
53830
|
-
|
|
53831
|
-
|
|
53832
|
-
|
|
53823
|
+
function startServerDetached() {
|
|
53824
|
+
const { cmd, args, shell } = resolveServerStart();
|
|
53825
|
+
rotateLog();
|
|
53826
|
+
const fd = openLogFd();
|
|
53827
|
+
const child = (0, import_node_child_process5.spawn)(cmd, args, {
|
|
53828
|
+
cwd: __dirname2,
|
|
53829
|
+
detached: true,
|
|
53830
|
+
stdio: fd == null ? "ignore" : ["ignore", fd, fd],
|
|
53831
|
+
windowsHide: true,
|
|
53832
|
+
shell
|
|
53833
|
+
});
|
|
53834
|
+
child.unref();
|
|
53833
53835
|
}
|
|
53834
|
-
function
|
|
53835
|
-
|
|
53836
|
-
|
|
53837
|
-
return override.split(";").map((d) => d.trim()).filter(Boolean).map((dir) => ({ runtime: "custom", dir }));
|
|
53838
|
-
}
|
|
53839
|
-
const home = (0, import_node_os9.homedir)();
|
|
53840
|
-
return [
|
|
53841
|
-
{ runtime: "claude", dir: (0, import_node_path13.join)(home, ".claude") },
|
|
53842
|
-
{ runtime: "codex", dir: (0, import_node_path13.join)(home, ".codex") },
|
|
53843
|
-
{ runtime: "opencode", dir: (0, import_node_path13.join)(home, ".opencode") }
|
|
53844
|
-
];
|
|
53836
|
+
function openBrowser2(url) {
|
|
53837
|
+
if (isWin2) (0, import_node_child_process5.spawn)("cmd", ["/c", "start", "", url], { windowsHide: true }).unref();
|
|
53838
|
+
else (0, import_node_child_process5.spawn)(process.platform === "darwin" ? "open" : "xdg-open", [url], { detached: true }).unref();
|
|
53845
53839
|
}
|
|
53846
|
-
function
|
|
53840
|
+
function stopServer() {
|
|
53841
|
+
if (!isWin2) {
|
|
53842
|
+
try {
|
|
53843
|
+
(0, import_node_child_process5.execSync)(`bash -lc "fuser -k ${PORT}/tcp"`, { stdio: "ignore" });
|
|
53844
|
+
return true;
|
|
53845
|
+
} catch {
|
|
53846
|
+
return false;
|
|
53847
|
+
}
|
|
53848
|
+
}
|
|
53847
53849
|
try {
|
|
53848
|
-
const
|
|
53849
|
-
|
|
53850
|
-
|
|
53851
|
-
const fm = (0, import_yaml5.parse)(m[1]);
|
|
53852
|
-
const meta = fm?.metadata;
|
|
53853
|
-
const v = meta?.version;
|
|
53854
|
-
return typeof v === "string" || typeof v === "number" ? String(v) : null;
|
|
53850
|
+
const ps = `$p = Get-NetTCPConnection -LocalPort ${PORT} -State Listen -ErrorAction SilentlyContinue | Select-Object -Expand OwningProcess -Unique; if ($p) { $p | ForEach-Object { taskkill /PID $_ /T /F } } else { exit 9 }`;
|
|
53851
|
+
(0, import_node_child_process5.execSync)(`powershell -NoProfile -Command "${ps}"`, { stdio: "ignore" });
|
|
53852
|
+
return true;
|
|
53855
53853
|
} catch {
|
|
53856
|
-
return
|
|
53854
|
+
return false;
|
|
53857
53855
|
}
|
|
53858
53856
|
}
|
|
53859
|
-
function
|
|
53860
|
-
|
|
53861
|
-
|
|
53862
|
-
|
|
53863
|
-
|
|
53864
|
-
|
|
53865
|
-
|
|
53866
|
-
const pa = split(a);
|
|
53867
|
-
const pb = split(b);
|
|
53868
|
-
const n = Math.max(pa.nums.length, pb.nums.length);
|
|
53869
|
-
for (let i = 0; i < n; i++) {
|
|
53870
|
-
const d = (pa.nums[i] ?? 0) - (pb.nums[i] ?? 0);
|
|
53871
|
-
if (d !== 0) return d > 0 ? 1 : -1;
|
|
53857
|
+
async function ensureServerUp() {
|
|
53858
|
+
if (await ping()) return;
|
|
53859
|
+
log("starting server\u2026");
|
|
53860
|
+
startServerDetached();
|
|
53861
|
+
if (!await waitHealthy()) {
|
|
53862
|
+
log(`server did not become healthy on ${HEALTH_URL} within 30s \u2014 check for errors with "npm run dev"`);
|
|
53863
|
+
process.exit(1);
|
|
53872
53864
|
}
|
|
53873
|
-
|
|
53874
|
-
if (pa.pre === "") return 1;
|
|
53875
|
-
if (pb.pre === "") return -1;
|
|
53876
|
-
return pa.pre < pb.pre ? -1 : 1;
|
|
53877
|
-
}
|
|
53878
|
-
function decideAction(installed, bundled) {
|
|
53879
|
-
if (installed === null) return "installed";
|
|
53880
|
-
const c = compareVersions(bundled, installed);
|
|
53881
|
-
if (c > 0) return "updated";
|
|
53882
|
-
if (c === 0) return "skipped-current";
|
|
53883
|
-
return "skipped-newer";
|
|
53865
|
+
log("server up");
|
|
53884
53866
|
}
|
|
53885
|
-
function
|
|
53886
|
-
|
|
53887
|
-
|
|
53888
|
-
|
|
53889
|
-
|
|
53890
|
-
|
|
53891
|
-
|
|
53892
|
-
|
|
53893
|
-
|
|
53894
|
-
|
|
53895
|
-
let isDir = false;
|
|
53896
|
-
try {
|
|
53897
|
-
isDir = (0, import_node_fs15.statSync)(dir).isDirectory();
|
|
53898
|
-
} catch {
|
|
53899
|
-
isDir = false;
|
|
53867
|
+
async function cmdOpen() {
|
|
53868
|
+
if (await ping()) {
|
|
53869
|
+
const running = await probeVersion();
|
|
53870
|
+
if (shouldTakeOver(running, _selfVersion)) {
|
|
53871
|
+
log(`floless.app v${running} is already running on ${PORT}; this build is v${_selfVersion} \u2014 taking over`);
|
|
53872
|
+
stopServer();
|
|
53873
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
53874
|
+
await ensureServerUp();
|
|
53875
|
+
} else {
|
|
53876
|
+
log(`already running${running ? ` (v${running})` : ""} \u2014 opening browser`);
|
|
53900
53877
|
}
|
|
53901
|
-
|
|
53902
|
-
|
|
53903
|
-
if (!v) continue;
|
|
53904
|
-
out.push({ name, dir, version: v });
|
|
53878
|
+
} else {
|
|
53879
|
+
await ensureServerUp();
|
|
53905
53880
|
}
|
|
53906
|
-
|
|
53881
|
+
log(`opening ${BROWSER_URL}`);
|
|
53882
|
+
openBrowser2(BROWSER_URL);
|
|
53907
53883
|
}
|
|
53908
|
-
function
|
|
53909
|
-
|
|
53910
|
-
|
|
53911
|
-
|
|
53912
|
-
const skills = bundledSkills(root);
|
|
53913
|
-
if (!skills.length) return results;
|
|
53914
|
-
for (const { runtime, dir: cfg } of targetConfigDirs()) {
|
|
53915
|
-
if (!(0, import_node_fs15.existsSync)(cfg)) continue;
|
|
53916
|
-
const skillsDir = (0, import_node_path13.join)(cfg, "skills");
|
|
53917
|
-
for (const s of skills) {
|
|
53918
|
-
const dest = (0, import_node_path13.join)(skillsDir, s.name);
|
|
53919
|
-
const installedMd = (0, import_node_path13.join)(dest, "SKILL.md");
|
|
53920
|
-
const installed = (0, import_node_fs15.existsSync)(installedMd) ? skillVersion(installedMd) : null;
|
|
53921
|
-
const action = decideAction(installed, s.version);
|
|
53922
|
-
if (action === "installed" || action === "updated") {
|
|
53923
|
-
try {
|
|
53924
|
-
if (action === "updated") (0, import_node_fs15.rmSync)(dest, { recursive: true, force: true });
|
|
53925
|
-
(0, import_node_fs15.cpSync)(s.dir, dest, { recursive: true });
|
|
53926
|
-
results.push({ runtime, skill: s.name, action, from: installed, to: s.version });
|
|
53927
|
-
} catch {
|
|
53928
|
-
}
|
|
53929
|
-
} else {
|
|
53930
|
-
results.push({ runtime, skill: s.name, action, from: installed, to: s.version });
|
|
53931
|
-
}
|
|
53932
|
-
}
|
|
53884
|
+
async function cmdStart() {
|
|
53885
|
+
if (await ping()) {
|
|
53886
|
+
log("already running");
|
|
53887
|
+
return;
|
|
53933
53888
|
}
|
|
53934
|
-
|
|
53889
|
+
await ensureServerUp();
|
|
53935
53890
|
}
|
|
53936
|
-
|
|
53937
|
-
|
|
53938
|
-
|
|
53939
|
-
|
|
53940
|
-
|
|
53941
|
-
|
|
53942
|
-
|
|
53943
|
-
|
|
53944
|
-
|
|
53945
|
-
|
|
53946
|
-
|
|
53947
|
-
|
|
53948
|
-
// node_modules/readdirp/esm/index.js
|
|
53949
|
-
var import_promises2 = require("node:fs/promises");
|
|
53950
|
-
var import_node_stream2 = require("node:stream");
|
|
53951
|
-
var import_node_path14 = require("node:path");
|
|
53952
|
-
var EntryTypes = {
|
|
53953
|
-
FILE_TYPE: "files",
|
|
53954
|
-
DIR_TYPE: "directories",
|
|
53955
|
-
FILE_DIR_TYPE: "files_directories",
|
|
53956
|
-
EVERYTHING_TYPE: "all"
|
|
53957
|
-
};
|
|
53958
|
-
var defaultOptions = {
|
|
53959
|
-
root: ".",
|
|
53960
|
-
fileFilter: (_entryInfo) => true,
|
|
53961
|
-
directoryFilter: (_entryInfo) => true,
|
|
53962
|
-
type: EntryTypes.FILE_TYPE,
|
|
53963
|
-
lstat: false,
|
|
53964
|
-
depth: 2147483648,
|
|
53965
|
-
alwaysStat: false,
|
|
53966
|
-
highWaterMark: 4096
|
|
53967
|
-
};
|
|
53968
|
-
Object.freeze(defaultOptions);
|
|
53969
|
-
var RECURSIVE_ERROR_CODE = "READDIRP_RECURSIVE_ERROR";
|
|
53970
|
-
var NORMAL_FLOW_ERRORS = /* @__PURE__ */ new Set(["ENOENT", "EPERM", "EACCES", "ELOOP", RECURSIVE_ERROR_CODE]);
|
|
53971
|
-
var ALL_TYPES = [
|
|
53972
|
-
EntryTypes.DIR_TYPE,
|
|
53973
|
-
EntryTypes.EVERYTHING_TYPE,
|
|
53974
|
-
EntryTypes.FILE_DIR_TYPE,
|
|
53975
|
-
EntryTypes.FILE_TYPE
|
|
53976
|
-
];
|
|
53977
|
-
var DIR_TYPES = /* @__PURE__ */ new Set([
|
|
53978
|
-
EntryTypes.DIR_TYPE,
|
|
53979
|
-
EntryTypes.EVERYTHING_TYPE,
|
|
53980
|
-
EntryTypes.FILE_DIR_TYPE
|
|
53981
|
-
]);
|
|
53982
|
-
var FILE_TYPES = /* @__PURE__ */ new Set([
|
|
53983
|
-
EntryTypes.EVERYTHING_TYPE,
|
|
53984
|
-
EntryTypes.FILE_DIR_TYPE,
|
|
53985
|
-
EntryTypes.FILE_TYPE
|
|
53986
|
-
]);
|
|
53987
|
-
var isNormalFlowError = (error) => NORMAL_FLOW_ERRORS.has(error.code);
|
|
53988
|
-
var wantBigintFsStats = process.platform === "win32";
|
|
53989
|
-
var emptyFn = (_entryInfo) => true;
|
|
53990
|
-
var normalizeFilter = (filter) => {
|
|
53991
|
-
if (filter === void 0)
|
|
53992
|
-
return emptyFn;
|
|
53993
|
-
if (typeof filter === "function")
|
|
53994
|
-
return filter;
|
|
53995
|
-
if (typeof filter === "string") {
|
|
53996
|
-
const fl = filter.trim();
|
|
53997
|
-
return (entry2) => entry2.basename === fl;
|
|
53998
|
-
}
|
|
53999
|
-
if (Array.isArray(filter)) {
|
|
54000
|
-
const trItems = filter.map((item) => item.trim());
|
|
54001
|
-
return (entry2) => trItems.some((f) => entry2.basename === f);
|
|
54002
|
-
}
|
|
54003
|
-
return emptyFn;
|
|
54004
|
-
};
|
|
54005
|
-
var ReaddirpStream = class extends import_node_stream2.Readable {
|
|
54006
|
-
constructor(options = {}) {
|
|
54007
|
-
super({
|
|
54008
|
-
objectMode: true,
|
|
54009
|
-
autoDestroy: true,
|
|
54010
|
-
highWaterMark: options.highWaterMark
|
|
53891
|
+
var SUPERVISE_POLL_MS = 3e3;
|
|
53892
|
+
var SUPERVISE_RESPAWN_ENV = "FLOLESS_SUPERVISE_RESPAWNED";
|
|
53893
|
+
async function cmdSupervise() {
|
|
53894
|
+
const isSeaChannel = /flolessapp\.exe$/i.test(process.execPath);
|
|
53895
|
+
if (isSeaChannel && !process.env[SUPERVISE_RESPAWN_ENV]) {
|
|
53896
|
+
rotateLog();
|
|
53897
|
+
const fd = openLogFd();
|
|
53898
|
+
const child = (0, import_node_child_process5.spawn)(process.execPath, ["--supervise"], {
|
|
53899
|
+
detached: true,
|
|
53900
|
+
stdio: fd == null ? "ignore" : ["ignore", fd, fd],
|
|
53901
|
+
windowsHide: true,
|
|
53902
|
+
env: { ...process.env, [SUPERVISE_RESPAWN_ENV]: "1" }
|
|
54011
53903
|
});
|
|
54012
|
-
|
|
54013
|
-
|
|
54014
|
-
|
|
54015
|
-
|
|
54016
|
-
|
|
54017
|
-
|
|
54018
|
-
|
|
54019
|
-
|
|
54020
|
-
|
|
53904
|
+
child.unref();
|
|
53905
|
+
log("supervisor re-spawned detached");
|
|
53906
|
+
return;
|
|
53907
|
+
}
|
|
53908
|
+
log("supervisor up \u2014 keeping the server alive");
|
|
53909
|
+
for (; ; ) {
|
|
53910
|
+
if (!await ping()) {
|
|
53911
|
+
log("server not responding \u2014 (re)starting");
|
|
53912
|
+
startServerDetached();
|
|
53913
|
+
log(await waitHealthy() ? "server up" : "server not healthy yet \u2014 will retry");
|
|
54021
53914
|
}
|
|
54022
|
-
|
|
54023
|
-
this._wantsDir = type ? DIR_TYPES.has(type) : false;
|
|
54024
|
-
this._wantsFile = type ? FILE_TYPES.has(type) : false;
|
|
54025
|
-
this._wantsEverything = type === EntryTypes.EVERYTHING_TYPE;
|
|
54026
|
-
this._root = (0, import_node_path14.resolve)(root);
|
|
54027
|
-
this._isDirent = !opts.alwaysStat;
|
|
54028
|
-
this._statsProp = this._isDirent ? "dirent" : "stats";
|
|
54029
|
-
this._rdOptions = { encoding: "utf8", withFileTypes: this._isDirent };
|
|
54030
|
-
this.parents = [this._exploreDir(root, 1)];
|
|
54031
|
-
this.reading = false;
|
|
54032
|
-
this.parent = void 0;
|
|
53915
|
+
await new Promise((r) => setTimeout(r, SUPERVISE_POLL_MS));
|
|
54033
53916
|
}
|
|
54034
|
-
|
|
54035
|
-
|
|
53917
|
+
}
|
|
53918
|
+
async function cmdStop() {
|
|
53919
|
+
if (!stopServer()) {
|
|
53920
|
+
log("not running \u2014 nothing to stop");
|
|
53921
|
+
return;
|
|
53922
|
+
}
|
|
53923
|
+
log(`stopping server on port ${PORT}\u2026`);
|
|
53924
|
+
for (let i = 0; i < 10; i++) {
|
|
53925
|
+
if (!await ping()) {
|
|
53926
|
+
log("stopped");
|
|
54036
53927
|
return;
|
|
54037
|
-
this.reading = true;
|
|
54038
|
-
try {
|
|
54039
|
-
while (!this.destroyed && batch > 0) {
|
|
54040
|
-
const par = this.parent;
|
|
54041
|
-
const fil = par && par.files;
|
|
54042
|
-
if (fil && fil.length > 0) {
|
|
54043
|
-
const { path, depth } = par;
|
|
54044
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path));
|
|
54045
|
-
const awaited = await Promise.all(slice);
|
|
54046
|
-
for (const entry2 of awaited) {
|
|
54047
|
-
if (!entry2)
|
|
54048
|
-
continue;
|
|
54049
|
-
if (this.destroyed)
|
|
54050
|
-
return;
|
|
54051
|
-
const entryType = await this._getEntryType(entry2);
|
|
54052
|
-
if (entryType === "directory" && this._directoryFilter(entry2)) {
|
|
54053
|
-
if (depth <= this._maxDepth) {
|
|
54054
|
-
this.parents.push(this._exploreDir(entry2.fullPath, depth + 1));
|
|
54055
|
-
}
|
|
54056
|
-
if (this._wantsDir) {
|
|
54057
|
-
this.push(entry2);
|
|
54058
|
-
batch--;
|
|
54059
|
-
}
|
|
54060
|
-
} else if ((entryType === "file" || this._includeAsFile(entry2)) && this._fileFilter(entry2)) {
|
|
54061
|
-
if (this._wantsFile) {
|
|
54062
|
-
this.push(entry2);
|
|
54063
|
-
batch--;
|
|
54064
|
-
}
|
|
54065
|
-
}
|
|
54066
|
-
}
|
|
54067
|
-
} else {
|
|
54068
|
-
const parent = this.parents.pop();
|
|
54069
|
-
if (!parent) {
|
|
54070
|
-
this.push(null);
|
|
54071
|
-
break;
|
|
54072
|
-
}
|
|
54073
|
-
this.parent = await parent;
|
|
54074
|
-
if (this.destroyed)
|
|
54075
|
-
return;
|
|
54076
|
-
}
|
|
54077
|
-
}
|
|
54078
|
-
} catch (error) {
|
|
54079
|
-
this.destroy(error);
|
|
54080
|
-
} finally {
|
|
54081
|
-
this.reading = false;
|
|
54082
53928
|
}
|
|
53929
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
54083
53930
|
}
|
|
54084
|
-
|
|
54085
|
-
|
|
54086
|
-
|
|
54087
|
-
|
|
54088
|
-
|
|
54089
|
-
|
|
54090
|
-
|
|
54091
|
-
|
|
53931
|
+
log("warning: a process may still be holding the port");
|
|
53932
|
+
}
|
|
53933
|
+
function parseTeardownFlags(argv = []) {
|
|
53934
|
+
return {
|
|
53935
|
+
purge: argv.includes("--purge"),
|
|
53936
|
+
keepAware: argv.includes("--keep-aware")
|
|
53937
|
+
};
|
|
53938
|
+
}
|
|
53939
|
+
function parseProcessList(json) {
|
|
53940
|
+
let parsed;
|
|
53941
|
+
try {
|
|
53942
|
+
parsed = JSON.parse(json || "[]");
|
|
53943
|
+
} catch {
|
|
53944
|
+
return [];
|
|
54092
53945
|
}
|
|
54093
|
-
|
|
54094
|
-
|
|
54095
|
-
|
|
53946
|
+
const arr = Array.isArray(parsed) ? parsed : [parsed];
|
|
53947
|
+
return arr.filter((p) => p && p.ProcessId != null).map((p) => ({ pid: Number(p.ProcessId), cmd: String(p.CommandLine ?? "") }));
|
|
53948
|
+
}
|
|
53949
|
+
function enumerateProcesses() {
|
|
53950
|
+
if (!isWin2) return [];
|
|
53951
|
+
try {
|
|
53952
|
+
const ps = "Get-CimInstance Win32_Process | Select-Object ProcessId,CommandLine | ConvertTo-Json -Compress";
|
|
53953
|
+
const out = (0, import_node_child_process5.execSync)(`powershell -NoProfile -Command "${ps}"`, {
|
|
53954
|
+
encoding: "utf8",
|
|
53955
|
+
windowsHide: true,
|
|
53956
|
+
maxBuffer: 16 * 1024 * 1024
|
|
53957
|
+
});
|
|
53958
|
+
return parseProcessList(out);
|
|
53959
|
+
} catch {
|
|
53960
|
+
return [];
|
|
53961
|
+
}
|
|
53962
|
+
}
|
|
53963
|
+
function taskkillArgs(pid, { tree = true } = {}) {
|
|
53964
|
+
return tree ? ["/PID", String(pid), "/T", "/F"] : ["/PID", String(pid), "/F"];
|
|
53965
|
+
}
|
|
53966
|
+
function killSupervisor({ tree = true } = {}) {
|
|
53967
|
+
if (!isWin2) return;
|
|
53968
|
+
const isNpmChannel = /^node(\.exe)?$/i.test((0, import_node_path13.basename)(process.execPath));
|
|
53969
|
+
const scriptMatch = isNpmChannel ? (0, import_node_path13.basename)((0, import_node_url.fileURLToPath)(__import_meta_url)) : void 0;
|
|
53970
|
+
const realExe = resolveRealInstallExe(process.execPath);
|
|
53971
|
+
const exeMatch = realExe === process.execPath ? process.execPath : [process.execPath, realExe];
|
|
53972
|
+
const pids = supervisorPidsToKill(enumerateProcesses(), process.pid, exeMatch, scriptMatch);
|
|
53973
|
+
for (const pid of pids) {
|
|
53974
|
+
log(`stopping supervisor (pid ${pid})\u2026`);
|
|
54096
53975
|
try {
|
|
54097
|
-
|
|
54098
|
-
|
|
54099
|
-
entry2[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
54100
|
-
} catch (err) {
|
|
54101
|
-
this._onError(err);
|
|
54102
|
-
return;
|
|
53976
|
+
(0, import_node_child_process5.execFileSync)("taskkill", taskkillArgs(pid, { tree }), { stdio: "ignore", windowsHide: true });
|
|
53977
|
+
} catch {
|
|
54103
53978
|
}
|
|
54104
|
-
return entry2;
|
|
54105
53979
|
}
|
|
54106
|
-
|
|
54107
|
-
|
|
54108
|
-
|
|
53980
|
+
}
|
|
53981
|
+
async function cmdTeardown() {
|
|
53982
|
+
killSupervisor();
|
|
53983
|
+
stopServer();
|
|
53984
|
+
const deadline = Date.now() + 3e3;
|
|
53985
|
+
while (Date.now() < deadline) {
|
|
53986
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
53987
|
+
if (await ping()) {
|
|
53988
|
+
log("server respawned after kill \u2014 re-killing supervisor and stopping again");
|
|
53989
|
+
killSupervisor();
|
|
53990
|
+
stopServer();
|
|
53991
|
+
}
|
|
53992
|
+
}
|
|
53993
|
+
if (await ping()) log("warning: a process may still be holding the port");
|
|
53994
|
+
else log("server and supervisor stopped");
|
|
53995
|
+
}
|
|
53996
|
+
async function cmdRestart() {
|
|
53997
|
+
await cmdStop();
|
|
53998
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
53999
|
+
await cmdOpen();
|
|
54000
|
+
}
|
|
54001
|
+
function apiJson(path, method = "GET") {
|
|
54002
|
+
return new Promise((resolve4, reject) => {
|
|
54003
|
+
const req = import_node_http.default.request(`http://127.0.0.1:${PORT}${path}`, { method, timeout: 5e3 }, (res) => {
|
|
54004
|
+
let body = "";
|
|
54005
|
+
res.on("data", (c) => body += c);
|
|
54006
|
+
res.on("end", () => {
|
|
54007
|
+
try {
|
|
54008
|
+
resolve4(JSON.parse(body || "{}"));
|
|
54009
|
+
} catch {
|
|
54010
|
+
resolve4({});
|
|
54011
|
+
}
|
|
54012
|
+
});
|
|
54013
|
+
});
|
|
54014
|
+
req.on("error", reject);
|
|
54015
|
+
req.on("timeout", () => {
|
|
54016
|
+
req.destroy();
|
|
54017
|
+
reject(new Error("timeout"));
|
|
54018
|
+
});
|
|
54019
|
+
req.end();
|
|
54020
|
+
});
|
|
54021
|
+
}
|
|
54022
|
+
async function cmdLogin() {
|
|
54023
|
+
if (!await ping()) {
|
|
54024
|
+
log("starting server\u2026");
|
|
54025
|
+
startServerDetached();
|
|
54026
|
+
if (!await waitHealthy()) {
|
|
54027
|
+
log('server did not start \u2014 try "npm run dev" to see errors');
|
|
54028
|
+
process.exit(1);
|
|
54029
|
+
}
|
|
54030
|
+
}
|
|
54031
|
+
const started = await apiJson("/api/license/start", "POST").catch(() => ({}));
|
|
54032
|
+
log(`opening sign-in in your browser${started.signInUrl ? `: ${started.signInUrl}` : ""}`);
|
|
54033
|
+
log("waiting for sign-in to complete\u2026");
|
|
54034
|
+
const deadline = Date.now() + 185e3;
|
|
54035
|
+
while (Date.now() < deadline) {
|
|
54036
|
+
const s = await apiJson("/api/license/status").catch(() => ({}));
|
|
54037
|
+
if (s.state === "valid" || s.state === "offline-grace") {
|
|
54038
|
+
log("signed in \u2014 subscription active");
|
|
54039
|
+
return;
|
|
54040
|
+
}
|
|
54041
|
+
if (s.state === "expired") {
|
|
54042
|
+
log(`signed in, but the subscription is expired \u2014 subscribe at ${s.signInUrl ?? "floless.io"}`);
|
|
54043
|
+
return;
|
|
54044
|
+
}
|
|
54045
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
54046
|
+
}
|
|
54047
|
+
log('sign-in timed out \u2014 run "floless login" again to retry');
|
|
54048
|
+
}
|
|
54049
|
+
function parseAction(arg) {
|
|
54050
|
+
let cmd = (arg || "open").toLowerCase();
|
|
54051
|
+
if (cmd.startsWith("floless:")) {
|
|
54052
|
+
try {
|
|
54053
|
+
cmd = (new URL(arg).hostname || "open").toLowerCase();
|
|
54054
|
+
} catch {
|
|
54055
|
+
cmd = "open";
|
|
54056
|
+
}
|
|
54057
|
+
}
|
|
54058
|
+
return cmd;
|
|
54059
|
+
}
|
|
54060
|
+
async function cmdUpdate() {
|
|
54061
|
+
log('this is the npm build \u2014 update it with "npm i -g @floless/app"');
|
|
54062
|
+
log('(the installer build self-updates via "floless-app update")');
|
|
54063
|
+
}
|
|
54064
|
+
function removeRegistryFootprint() {
|
|
54065
|
+
if (!isWin2) return;
|
|
54066
|
+
try {
|
|
54067
|
+
(0, import_node_child_process5.execFileSync)("reg", ["delete", RUN_KEY, "/v", RUN_VALUE, "/f"], { stdio: "ignore", windowsHide: true });
|
|
54068
|
+
} catch {
|
|
54069
|
+
}
|
|
54070
|
+
try {
|
|
54071
|
+
(0, import_node_child_process5.execFileSync)("reg", ["delete", PROTOCOL_KEY, "/f"], { stdio: "ignore", windowsHide: true });
|
|
54072
|
+
} catch {
|
|
54073
|
+
}
|
|
54074
|
+
}
|
|
54075
|
+
function promptYesNo(question) {
|
|
54076
|
+
return new Promise((resolve4) => {
|
|
54077
|
+
const rl = (0, import_node_readline.createInterface)({ input: process.stdin, output: process.stdout });
|
|
54078
|
+
rl.question(`${question} `, (answer) => {
|
|
54079
|
+
rl.close();
|
|
54080
|
+
resolve4(/^y(es)?$/i.test((answer ?? "").trim()));
|
|
54081
|
+
});
|
|
54082
|
+
});
|
|
54083
|
+
}
|
|
54084
|
+
async function cmdUninstall(flags = {}) {
|
|
54085
|
+
const decision = teardownDecision({ ...flags, isTTY: process.stdin.isTTY });
|
|
54086
|
+
log("stopping the server and supervisor\u2026");
|
|
54087
|
+
await cmdTeardown();
|
|
54088
|
+
log("removing the autostart entry and floless:// scheme\u2026");
|
|
54089
|
+
removeRegistryFootprint();
|
|
54090
|
+
let removeAware = decision.removeAware;
|
|
54091
|
+
if (decision.prompt) {
|
|
54092
|
+
removeAware = await promptYesNo("Also remove the AWARE runtime (@aware-aeco/cli)? [y/N]");
|
|
54093
|
+
}
|
|
54094
|
+
if (removeAware) {
|
|
54095
|
+
log("removing the AWARE runtime \u2014 this will affect ANY other tool that uses @aware-aeco/cli.");
|
|
54096
|
+
try {
|
|
54097
|
+
(0, import_node_child_process5.execSync)("npm uninstall -g @aware-aeco/cli", { stdio: "inherit", shell: isWin2 });
|
|
54098
|
+
} catch {
|
|
54099
|
+
log('warning: "npm uninstall -g @aware-aeco/cli" failed \u2014 see the output above.');
|
|
54100
|
+
}
|
|
54101
|
+
let lsJson = "";
|
|
54102
|
+
try {
|
|
54103
|
+
lsJson = (0, import_node_child_process5.execSync)("npm ls -g @aware-aeco/cli --depth=0 --json", {
|
|
54104
|
+
encoding: "utf8",
|
|
54105
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
54106
|
+
shell: isWin2
|
|
54107
|
+
});
|
|
54108
|
+
} catch (err) {
|
|
54109
|
+
lsJson = String(err?.stdout ?? "");
|
|
54110
|
+
}
|
|
54111
|
+
const stillPresent = awareIsPresent(lsJson);
|
|
54112
|
+
if (stillPresent) {
|
|
54113
|
+
log("AWARE may still be installed \u2014 remove it manually with: npm uninstall -g @aware-aeco/cli");
|
|
54114
|
+
log("(if that fails with a permission error, retry in an elevated shell).");
|
|
54115
|
+
} else {
|
|
54116
|
+
log("AWARE runtime removed.");
|
|
54117
|
+
}
|
|
54118
|
+
} else {
|
|
54119
|
+
log("keeping the AWARE runtime (@aware-aeco/cli). Remove it later with: npm uninstall -g @aware-aeco/cli");
|
|
54120
|
+
}
|
|
54121
|
+
log("done. Finally, remove floless.app itself with: npm uninstall -g @floless/app");
|
|
54122
|
+
}
|
|
54123
|
+
var ACTIONS = { open: cmdOpen, start: cmdStart, supervise: cmdSupervise, stop: cmdStop, restart: cmdRestart, login: cmdLogin, update: cmdUpdate, uninstall: cmdUninstall };
|
|
54124
|
+
async function runAction(arg, flagArgv = [], selfVersion = null) {
|
|
54125
|
+
_selfVersion = selfVersion;
|
|
54126
|
+
const cmd = parseAction(arg);
|
|
54127
|
+
const action = ACTIONS[cmd];
|
|
54128
|
+
if (!action) {
|
|
54129
|
+
log(`unknown command "${cmd}" \u2014 use: open | start | supervise | stop | restart | login | update | uninstall`);
|
|
54130
|
+
process.exit(2);
|
|
54131
|
+
}
|
|
54132
|
+
await action(parseTeardownFlags(flagArgv));
|
|
54133
|
+
}
|
|
54134
|
+
var entry = (0, import_node_path13.basename)(process.argv[1] ?? "").toLowerCase();
|
|
54135
|
+
if (entry === "launch.mjs") {
|
|
54136
|
+
runAction(process.argv[2], process.argv.slice(3)).catch((e) => {
|
|
54137
|
+
log(`error: ${e?.message ?? e}`);
|
|
54138
|
+
process.exit(1);
|
|
54139
|
+
});
|
|
54140
|
+
}
|
|
54141
|
+
|
|
54142
|
+
// aware-update.ts
|
|
54143
|
+
var REGISTRY_LATEST = "https://registry.npmjs.org/@aware-aeco/cli/latest";
|
|
54144
|
+
var FEED_TIMEOUT_MS2 = 15e3;
|
|
54145
|
+
var VERSION_TTL_MS = 5e3;
|
|
54146
|
+
var _v = null;
|
|
54147
|
+
function awareInstalledVersion(probe = aware.npmVersion) {
|
|
54148
|
+
const now = Date.now();
|
|
54149
|
+
if (_v && now - _v.at < VERSION_TTL_MS) return _v.val;
|
|
54150
|
+
_v = { at: now, val: probe() };
|
|
54151
|
+
return _v.val;
|
|
54152
|
+
}
|
|
54153
|
+
function invalidateAwareVersion() {
|
|
54154
|
+
_v = null;
|
|
54155
|
+
}
|
|
54156
|
+
function versionGt2(a, b) {
|
|
54157
|
+
const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
|
|
54158
|
+
const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
|
|
54159
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
54160
|
+
const d = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
54161
|
+
if (d !== 0) return d > 0;
|
|
54162
|
+
}
|
|
54163
|
+
return false;
|
|
54164
|
+
}
|
|
54165
|
+
function decideAwareUpdate(cur, latest) {
|
|
54166
|
+
const base = { supported: true, currentVersion: cur };
|
|
54167
|
+
if (!latest) return { ...base, updateAvailable: false, reason: "could not read the npm registry latest" };
|
|
54168
|
+
if (!cur) return { ...base, updateAvailable: false, targetVersion: latest, reason: "AWARE not installed yet" };
|
|
54169
|
+
if (versionGt2(latest, cur)) return { ...base, updateAvailable: true, targetVersion: latest };
|
|
54170
|
+
return { ...base, updateAvailable: false, targetVersion: latest, reason: `already up to date (v${cur})` };
|
|
54171
|
+
}
|
|
54172
|
+
async function fetchAwareLatest() {
|
|
54173
|
+
try {
|
|
54174
|
+
const res = await fetch(REGISTRY_LATEST, { redirect: "follow", signal: AbortSignal.timeout(FEED_TIMEOUT_MS2) });
|
|
54175
|
+
if (!res.ok) return null;
|
|
54176
|
+
const body = await res.json();
|
|
54177
|
+
return typeof body.version === "string" ? body.version : null;
|
|
54178
|
+
} catch {
|
|
54179
|
+
return null;
|
|
54180
|
+
}
|
|
54181
|
+
}
|
|
54182
|
+
async function checkAwareUpdate() {
|
|
54183
|
+
const cur = awareInstalledVersion();
|
|
54184
|
+
const latest = await fetchAwareLatest();
|
|
54185
|
+
return decideAwareUpdate(cur, latest);
|
|
54186
|
+
}
|
|
54187
|
+
async function applyAwareUpdate(deps) {
|
|
54188
|
+
try {
|
|
54189
|
+
await deps.install("latest");
|
|
54190
|
+
deps.refresh();
|
|
54191
|
+
invalidateAwareVersion();
|
|
54192
|
+
return { ok: true, version: deps.probe() };
|
|
54193
|
+
} catch (err) {
|
|
54194
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
54195
|
+
}
|
|
54196
|
+
}
|
|
54197
|
+
function awareApplyDeps(install) {
|
|
54198
|
+
return { install, refresh: refreshInvoker, probe: aware.npmVersion };
|
|
54199
|
+
}
|
|
54200
|
+
function awareUpgradeBlockReason(s) {
|
|
54201
|
+
if (s.bootstrapInstalling || s.installInFlight) return "an AWARE install is already in progress \u2014 try again in a moment";
|
|
54202
|
+
if (s.runActive) return "a workflow run is in progress \u2014 wait for it to finish before upgrading AWARE";
|
|
54203
|
+
if (s.hostWatcherActive) return "a live trigger/watch session is using the host \u2014 stop it before upgrading AWARE";
|
|
54204
|
+
return null;
|
|
54205
|
+
}
|
|
54206
|
+
|
|
54207
|
+
// skill-sync.ts
|
|
54208
|
+
var import_node_fs16 = require("node:fs");
|
|
54209
|
+
var import_node_os9 = require("node:os");
|
|
54210
|
+
var import_node_path14 = require("node:path");
|
|
54211
|
+
var import_node_url2 = require("node:url");
|
|
54212
|
+
var import_yaml5 = __toESM(require_dist6(), 1);
|
|
54213
|
+
|
|
54214
|
+
// build/ship-skills.mjs
|
|
54215
|
+
var PRODUCT_SKILLS = [
|
|
54216
|
+
"floless-app-bridge",
|
|
54217
|
+
// drive the floless.app CLI / desktop bridge from the user's AI
|
|
54218
|
+
"floless-app-onboarding",
|
|
54219
|
+
// guided, re-runnable tour of AWARE + floless.app for new users
|
|
54220
|
+
"floless-app-routines",
|
|
54221
|
+
// author event-driven ("on trigger") routines
|
|
54222
|
+
"floless-app-workflows"
|
|
54223
|
+
// author/run .flo workflows
|
|
54224
|
+
];
|
|
54225
|
+
function selectShippedSkillNames(names) {
|
|
54226
|
+
const present = new Set(names);
|
|
54227
|
+
return PRODUCT_SKILLS.filter((name) => present.has(name));
|
|
54228
|
+
}
|
|
54229
|
+
|
|
54230
|
+
// skill-sync.ts
|
|
54231
|
+
var __dirname3 = (0, import_node_path14.dirname)((0, import_node_url2.fileURLToPath)(__import_meta_url));
|
|
54232
|
+
function bundledSkillsRoot() {
|
|
54233
|
+
const candidates = [
|
|
54234
|
+
(0, import_node_path14.join)(__dirname3, "skills"),
|
|
54235
|
+
(0, import_node_path14.join)((0, import_node_path14.dirname)(process.execPath), "skills"),
|
|
54236
|
+
(0, import_node_path14.join)(__dirname3, "..", ".claude", "skills")
|
|
54237
|
+
];
|
|
54238
|
+
return candidates.find((p) => (0, import_node_fs16.existsSync)(p)) ?? null;
|
|
54239
|
+
}
|
|
54240
|
+
function targetConfigDirs() {
|
|
54241
|
+
const override = process.env.FLOLESS_SKILL_TARGETS;
|
|
54242
|
+
if (override) {
|
|
54243
|
+
return override.split(";").map((d) => d.trim()).filter(Boolean).map((dir) => ({ runtime: "custom", dir }));
|
|
54244
|
+
}
|
|
54245
|
+
const home = (0, import_node_os9.homedir)();
|
|
54246
|
+
return [
|
|
54247
|
+
{ runtime: "claude", dir: (0, import_node_path14.join)(home, ".claude") },
|
|
54248
|
+
{ runtime: "codex", dir: (0, import_node_path14.join)(home, ".codex") },
|
|
54249
|
+
{ runtime: "opencode", dir: (0, import_node_path14.join)(home, ".opencode") }
|
|
54250
|
+
];
|
|
54251
|
+
}
|
|
54252
|
+
function skillVersion(skillMdPath) {
|
|
54253
|
+
try {
|
|
54254
|
+
const text = (0, import_node_fs16.readFileSync)(skillMdPath, "utf8");
|
|
54255
|
+
const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(text);
|
|
54256
|
+
if (!m || m[1] === void 0) return null;
|
|
54257
|
+
const fm = (0, import_yaml5.parse)(m[1]);
|
|
54258
|
+
const meta = fm?.metadata;
|
|
54259
|
+
const v = meta?.version;
|
|
54260
|
+
return typeof v === "string" || typeof v === "number" ? String(v) : null;
|
|
54261
|
+
} catch {
|
|
54262
|
+
return null;
|
|
54263
|
+
}
|
|
54264
|
+
}
|
|
54265
|
+
function compareVersions(a, b) {
|
|
54266
|
+
const split = (v) => {
|
|
54267
|
+
const dash = v.indexOf("-");
|
|
54268
|
+
const core = dash >= 0 ? v.slice(0, dash) : v;
|
|
54269
|
+
const pre = dash >= 0 ? v.slice(dash + 1) : "";
|
|
54270
|
+
return { nums: core.split(".").map((s) => parseInt(s, 10) || 0), pre };
|
|
54271
|
+
};
|
|
54272
|
+
const pa = split(a);
|
|
54273
|
+
const pb = split(b);
|
|
54274
|
+
const n = Math.max(pa.nums.length, pb.nums.length);
|
|
54275
|
+
for (let i = 0; i < n; i++) {
|
|
54276
|
+
const d = (pa.nums[i] ?? 0) - (pb.nums[i] ?? 0);
|
|
54277
|
+
if (d !== 0) return d > 0 ? 1 : -1;
|
|
54278
|
+
}
|
|
54279
|
+
if (pa.pre === pb.pre) return 0;
|
|
54280
|
+
if (pa.pre === "") return 1;
|
|
54281
|
+
if (pb.pre === "") return -1;
|
|
54282
|
+
return pa.pre < pb.pre ? -1 : 1;
|
|
54283
|
+
}
|
|
54284
|
+
function decideAction(installed, bundled) {
|
|
54285
|
+
if (installed === null) return "installed";
|
|
54286
|
+
const c = compareVersions(bundled, installed);
|
|
54287
|
+
if (c > 0) return "updated";
|
|
54288
|
+
if (c === 0) return "skipped-current";
|
|
54289
|
+
return "skipped-newer";
|
|
54290
|
+
}
|
|
54291
|
+
function bundledSkills(root) {
|
|
54292
|
+
let entries = [];
|
|
54293
|
+
try {
|
|
54294
|
+
entries = selectShippedSkillNames((0, import_node_fs16.readdirSync)(root));
|
|
54295
|
+
} catch {
|
|
54296
|
+
return [];
|
|
54297
|
+
}
|
|
54298
|
+
const out = [];
|
|
54299
|
+
for (const name of entries) {
|
|
54300
|
+
const dir = (0, import_node_path14.join)(root, name);
|
|
54301
|
+
let isDir = false;
|
|
54302
|
+
try {
|
|
54303
|
+
isDir = (0, import_node_fs16.statSync)(dir).isDirectory();
|
|
54304
|
+
} catch {
|
|
54305
|
+
isDir = false;
|
|
54306
|
+
}
|
|
54307
|
+
if (!isDir) continue;
|
|
54308
|
+
const v = skillVersion((0, import_node_path14.join)(dir, "SKILL.md"));
|
|
54309
|
+
if (!v) continue;
|
|
54310
|
+
out.push({ name, dir, version: v });
|
|
54311
|
+
}
|
|
54312
|
+
return out;
|
|
54313
|
+
}
|
|
54314
|
+
function syncSkills() {
|
|
54315
|
+
const results = [];
|
|
54316
|
+
const root = bundledSkillsRoot();
|
|
54317
|
+
if (!root) return results;
|
|
54318
|
+
const skills = bundledSkills(root);
|
|
54319
|
+
if (!skills.length) return results;
|
|
54320
|
+
for (const { runtime, dir: cfg } of targetConfigDirs()) {
|
|
54321
|
+
if (!(0, import_node_fs16.existsSync)(cfg)) continue;
|
|
54322
|
+
const skillsDir = (0, import_node_path14.join)(cfg, "skills");
|
|
54323
|
+
for (const s of skills) {
|
|
54324
|
+
const dest = (0, import_node_path14.join)(skillsDir, s.name);
|
|
54325
|
+
const installedMd = (0, import_node_path14.join)(dest, "SKILL.md");
|
|
54326
|
+
const installed = (0, import_node_fs16.existsSync)(installedMd) ? skillVersion(installedMd) : null;
|
|
54327
|
+
const action = decideAction(installed, s.version);
|
|
54328
|
+
if (action === "installed" || action === "updated") {
|
|
54329
|
+
try {
|
|
54330
|
+
if (action === "updated") (0, import_node_fs16.rmSync)(dest, { recursive: true, force: true });
|
|
54331
|
+
(0, import_node_fs16.cpSync)(s.dir, dest, { recursive: true });
|
|
54332
|
+
results.push({ runtime, skill: s.name, action, from: installed, to: s.version });
|
|
54333
|
+
} catch {
|
|
54334
|
+
}
|
|
54335
|
+
} else {
|
|
54336
|
+
results.push({ runtime, skill: s.name, action, from: installed, to: s.version });
|
|
54337
|
+
}
|
|
54338
|
+
}
|
|
54339
|
+
}
|
|
54340
|
+
return results;
|
|
54341
|
+
}
|
|
54342
|
+
|
|
54343
|
+
// watch.ts
|
|
54344
|
+
var import_node_os10 = require("node:os");
|
|
54345
|
+
var import_node_path16 = require("node:path");
|
|
54346
|
+
var import_node_fs17 = require("node:fs");
|
|
54347
|
+
|
|
54348
|
+
// node_modules/chokidar/esm/index.js
|
|
54349
|
+
var import_fs2 = require("fs");
|
|
54350
|
+
var import_promises4 = require("fs/promises");
|
|
54351
|
+
var import_events = require("events");
|
|
54352
|
+
var sysPath2 = __toESM(require("path"), 1);
|
|
54353
|
+
|
|
54354
|
+
// node_modules/readdirp/esm/index.js
|
|
54355
|
+
var import_promises2 = require("node:fs/promises");
|
|
54356
|
+
var import_node_stream2 = require("node:stream");
|
|
54357
|
+
var import_node_path15 = require("node:path");
|
|
54358
|
+
var EntryTypes = {
|
|
54359
|
+
FILE_TYPE: "files",
|
|
54360
|
+
DIR_TYPE: "directories",
|
|
54361
|
+
FILE_DIR_TYPE: "files_directories",
|
|
54362
|
+
EVERYTHING_TYPE: "all"
|
|
54363
|
+
};
|
|
54364
|
+
var defaultOptions = {
|
|
54365
|
+
root: ".",
|
|
54366
|
+
fileFilter: (_entryInfo) => true,
|
|
54367
|
+
directoryFilter: (_entryInfo) => true,
|
|
54368
|
+
type: EntryTypes.FILE_TYPE,
|
|
54369
|
+
lstat: false,
|
|
54370
|
+
depth: 2147483648,
|
|
54371
|
+
alwaysStat: false,
|
|
54372
|
+
highWaterMark: 4096
|
|
54373
|
+
};
|
|
54374
|
+
Object.freeze(defaultOptions);
|
|
54375
|
+
var RECURSIVE_ERROR_CODE = "READDIRP_RECURSIVE_ERROR";
|
|
54376
|
+
var NORMAL_FLOW_ERRORS = /* @__PURE__ */ new Set(["ENOENT", "EPERM", "EACCES", "ELOOP", RECURSIVE_ERROR_CODE]);
|
|
54377
|
+
var ALL_TYPES = [
|
|
54378
|
+
EntryTypes.DIR_TYPE,
|
|
54379
|
+
EntryTypes.EVERYTHING_TYPE,
|
|
54380
|
+
EntryTypes.FILE_DIR_TYPE,
|
|
54381
|
+
EntryTypes.FILE_TYPE
|
|
54382
|
+
];
|
|
54383
|
+
var DIR_TYPES = /* @__PURE__ */ new Set([
|
|
54384
|
+
EntryTypes.DIR_TYPE,
|
|
54385
|
+
EntryTypes.EVERYTHING_TYPE,
|
|
54386
|
+
EntryTypes.FILE_DIR_TYPE
|
|
54387
|
+
]);
|
|
54388
|
+
var FILE_TYPES = /* @__PURE__ */ new Set([
|
|
54389
|
+
EntryTypes.EVERYTHING_TYPE,
|
|
54390
|
+
EntryTypes.FILE_DIR_TYPE,
|
|
54391
|
+
EntryTypes.FILE_TYPE
|
|
54392
|
+
]);
|
|
54393
|
+
var isNormalFlowError = (error) => NORMAL_FLOW_ERRORS.has(error.code);
|
|
54394
|
+
var wantBigintFsStats = process.platform === "win32";
|
|
54395
|
+
var emptyFn = (_entryInfo) => true;
|
|
54396
|
+
var normalizeFilter = (filter) => {
|
|
54397
|
+
if (filter === void 0)
|
|
54398
|
+
return emptyFn;
|
|
54399
|
+
if (typeof filter === "function")
|
|
54400
|
+
return filter;
|
|
54401
|
+
if (typeof filter === "string") {
|
|
54402
|
+
const fl = filter.trim();
|
|
54403
|
+
return (entry2) => entry2.basename === fl;
|
|
54404
|
+
}
|
|
54405
|
+
if (Array.isArray(filter)) {
|
|
54406
|
+
const trItems = filter.map((item) => item.trim());
|
|
54407
|
+
return (entry2) => trItems.some((f) => entry2.basename === f);
|
|
54408
|
+
}
|
|
54409
|
+
return emptyFn;
|
|
54410
|
+
};
|
|
54411
|
+
var ReaddirpStream = class extends import_node_stream2.Readable {
|
|
54412
|
+
constructor(options = {}) {
|
|
54413
|
+
super({
|
|
54414
|
+
objectMode: true,
|
|
54415
|
+
autoDestroy: true,
|
|
54416
|
+
highWaterMark: options.highWaterMark
|
|
54417
|
+
});
|
|
54418
|
+
const opts = { ...defaultOptions, ...options };
|
|
54419
|
+
const { root, type } = opts;
|
|
54420
|
+
this._fileFilter = normalizeFilter(opts.fileFilter);
|
|
54421
|
+
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
54422
|
+
const statMethod = opts.lstat ? import_promises2.lstat : import_promises2.stat;
|
|
54423
|
+
if (wantBigintFsStats) {
|
|
54424
|
+
this._stat = (path) => statMethod(path, { bigint: true });
|
|
54425
|
+
} else {
|
|
54426
|
+
this._stat = statMethod;
|
|
54427
|
+
}
|
|
54428
|
+
this._maxDepth = opts.depth ?? defaultOptions.depth;
|
|
54429
|
+
this._wantsDir = type ? DIR_TYPES.has(type) : false;
|
|
54430
|
+
this._wantsFile = type ? FILE_TYPES.has(type) : false;
|
|
54431
|
+
this._wantsEverything = type === EntryTypes.EVERYTHING_TYPE;
|
|
54432
|
+
this._root = (0, import_node_path15.resolve)(root);
|
|
54433
|
+
this._isDirent = !opts.alwaysStat;
|
|
54434
|
+
this._statsProp = this._isDirent ? "dirent" : "stats";
|
|
54435
|
+
this._rdOptions = { encoding: "utf8", withFileTypes: this._isDirent };
|
|
54436
|
+
this.parents = [this._exploreDir(root, 1)];
|
|
54437
|
+
this.reading = false;
|
|
54438
|
+
this.parent = void 0;
|
|
54439
|
+
}
|
|
54440
|
+
async _read(batch) {
|
|
54441
|
+
if (this.reading)
|
|
54442
|
+
return;
|
|
54443
|
+
this.reading = true;
|
|
54444
|
+
try {
|
|
54445
|
+
while (!this.destroyed && batch > 0) {
|
|
54446
|
+
const par = this.parent;
|
|
54447
|
+
const fil = par && par.files;
|
|
54448
|
+
if (fil && fil.length > 0) {
|
|
54449
|
+
const { path, depth } = par;
|
|
54450
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path));
|
|
54451
|
+
const awaited = await Promise.all(slice);
|
|
54452
|
+
for (const entry2 of awaited) {
|
|
54453
|
+
if (!entry2)
|
|
54454
|
+
continue;
|
|
54455
|
+
if (this.destroyed)
|
|
54456
|
+
return;
|
|
54457
|
+
const entryType = await this._getEntryType(entry2);
|
|
54458
|
+
if (entryType === "directory" && this._directoryFilter(entry2)) {
|
|
54459
|
+
if (depth <= this._maxDepth) {
|
|
54460
|
+
this.parents.push(this._exploreDir(entry2.fullPath, depth + 1));
|
|
54461
|
+
}
|
|
54462
|
+
if (this._wantsDir) {
|
|
54463
|
+
this.push(entry2);
|
|
54464
|
+
batch--;
|
|
54465
|
+
}
|
|
54466
|
+
} else if ((entryType === "file" || this._includeAsFile(entry2)) && this._fileFilter(entry2)) {
|
|
54467
|
+
if (this._wantsFile) {
|
|
54468
|
+
this.push(entry2);
|
|
54469
|
+
batch--;
|
|
54470
|
+
}
|
|
54471
|
+
}
|
|
54472
|
+
}
|
|
54473
|
+
} else {
|
|
54474
|
+
const parent = this.parents.pop();
|
|
54475
|
+
if (!parent) {
|
|
54476
|
+
this.push(null);
|
|
54477
|
+
break;
|
|
54478
|
+
}
|
|
54479
|
+
this.parent = await parent;
|
|
54480
|
+
if (this.destroyed)
|
|
54481
|
+
return;
|
|
54482
|
+
}
|
|
54483
|
+
}
|
|
54484
|
+
} catch (error) {
|
|
54485
|
+
this.destroy(error);
|
|
54486
|
+
} finally {
|
|
54487
|
+
this.reading = false;
|
|
54488
|
+
}
|
|
54489
|
+
}
|
|
54490
|
+
async _exploreDir(path, depth) {
|
|
54491
|
+
let files;
|
|
54492
|
+
try {
|
|
54493
|
+
files = await (0, import_promises2.readdir)(path, this._rdOptions);
|
|
54494
|
+
} catch (error) {
|
|
54495
|
+
this._onError(error);
|
|
54496
|
+
}
|
|
54497
|
+
return { files, depth, path };
|
|
54498
|
+
}
|
|
54499
|
+
async _formatEntry(dirent, path) {
|
|
54500
|
+
let entry2;
|
|
54501
|
+
const basename5 = this._isDirent ? dirent.name : dirent;
|
|
54502
|
+
try {
|
|
54503
|
+
const fullPath = (0, import_node_path15.resolve)((0, import_node_path15.join)(path, basename5));
|
|
54504
|
+
entry2 = { path: (0, import_node_path15.relative)(this._root, fullPath), fullPath, basename: basename5 };
|
|
54505
|
+
entry2[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
54506
|
+
} catch (err) {
|
|
54507
|
+
this._onError(err);
|
|
54508
|
+
return;
|
|
54509
|
+
}
|
|
54510
|
+
return entry2;
|
|
54511
|
+
}
|
|
54512
|
+
_onError(err) {
|
|
54513
|
+
if (isNormalFlowError(err) && !this.destroyed) {
|
|
54514
|
+
this.emit("warn", err);
|
|
54109
54515
|
} else {
|
|
54110
54516
|
this.destroy(err);
|
|
54111
54517
|
}
|
|
@@ -54129,7 +54535,7 @@ var ReaddirpStream = class extends import_node_stream2.Readable {
|
|
|
54129
54535
|
}
|
|
54130
54536
|
if (entryRealPathStats.isDirectory()) {
|
|
54131
54537
|
const len = entryRealPath.length;
|
|
54132
|
-
if (full.startsWith(entryRealPath) && full.substr(len, 1) ===
|
|
54538
|
+
if (full.startsWith(entryRealPath) && full.substr(len, 1) === import_node_path15.sep) {
|
|
54133
54539
|
const recursiveError = new Error(`Circular symlink detected: "${full}" points to "${entryRealPath}"`);
|
|
54134
54540
|
recursiveError.code = RECURSIVE_ERROR_CODE;
|
|
54135
54541
|
return this._onError(recursiveError);
|
|
@@ -55614,1081 +56020,684 @@ var FSWatcher = class extends import_events.EventEmitter {
|
|
|
55614
56020
|
stream.once(STR_CLOSE, () => {
|
|
55615
56021
|
stream = void 0;
|
|
55616
56022
|
});
|
|
55617
|
-
stream.once(STR_END, () => {
|
|
55618
|
-
if (stream) {
|
|
55619
|
-
this._streams.delete(stream);
|
|
55620
|
-
stream = void 0;
|
|
55621
|
-
}
|
|
55622
|
-
});
|
|
55623
|
-
return stream;
|
|
55624
|
-
}
|
|
55625
|
-
};
|
|
55626
|
-
function watch(paths, options = {}) {
|
|
55627
|
-
const watcher = new FSWatcher(options);
|
|
55628
|
-
watcher.add(paths);
|
|
55629
|
-
return watcher;
|
|
55630
|
-
}
|
|
55631
|
-
var esm_default = { watch, FSWatcher };
|
|
55632
|
-
|
|
55633
|
-
// watch.ts
|
|
55634
|
-
function appIdFromLogPath(path) {
|
|
55635
|
-
const parts = path.split(/[\\/]/);
|
|
55636
|
-
const i = parts.lastIndexOf("logs");
|
|
55637
|
-
return i >= 0 && parts[i + 1] ? parts[i + 1] : null;
|
|
55638
|
-
}
|
|
55639
|
-
function startWatcher() {
|
|
55640
|
-
const awareDir = process.env.AWARE_HOME ?? (0, import_node_path15.join)((0, import_node_os10.homedir)(), ".aware");
|
|
55641
|
-
const credentialsDir = (0, import_node_path15.join)(awareDir, "credentials");
|
|
55642
|
-
if (!(0, import_node_fs16.existsSync)(credentialsDir)) {
|
|
55643
|
-
try {
|
|
55644
|
-
(0, import_node_fs16.mkdirSync)(credentialsDir, { recursive: true });
|
|
55645
|
-
} catch {
|
|
55646
|
-
}
|
|
55647
|
-
}
|
|
55648
|
-
const targets = ["apps", "logs", "credentials"].map((d) => (0, import_node_path15.join)(awareDir, d)).filter((p) => (0, import_node_fs16.existsSync)(p));
|
|
55649
|
-
if (targets.length === 0) {
|
|
55650
|
-
return null;
|
|
55651
|
-
}
|
|
55652
|
-
const watcher = esm_default.watch(targets, {
|
|
55653
|
-
ignoreInitial: true,
|
|
55654
|
-
awaitWriteFinish: { stabilityThreshold: 150, pollInterval: 50 }
|
|
55655
|
-
});
|
|
55656
|
-
watcher.on("all", (event, path) => {
|
|
55657
|
-
const isCredential = path.split(/[\\/]/).includes("credentials");
|
|
55658
|
-
const kind = isCredential ? "credential" : path.endsWith(".jsonl") ? "trace" : path.endsWith(".lock") ? "lock" : path.endsWith(".flo") || path.endsWith(".app") ? "source" : "file";
|
|
55659
|
-
broadcast({ type: "fs-change", kind, event, path });
|
|
55660
|
-
if (kind === "trace" && event !== "unlink" && (0, import_node_fs16.existsSync)(path)) {
|
|
55661
|
-
const id = appIdFromLogPath(path);
|
|
55662
|
-
if (!id) return;
|
|
55663
|
-
try {
|
|
55664
|
-
broadcast({ type: "trace-file", id, runId: path.split(import_node_path15.sep).pop()?.replace(/\.jsonl$/, "") ?? null, events: parseTrace((0, import_node_fs16.readFileSync)(path, "utf8")) });
|
|
55665
|
-
} catch {
|
|
55666
|
-
}
|
|
55667
|
-
}
|
|
55668
|
-
});
|
|
55669
|
-
return { close: () => watcher.close() };
|
|
55670
|
-
}
|
|
55671
|
-
|
|
55672
|
-
// index.ts
|
|
55673
|
-
var __dirname3 = (0, import_node_path16.dirname)((0, import_node_url2.fileURLToPath)(__import_meta_url));
|
|
55674
|
-
var WEB_ROOT = [(0, import_node_path16.join)(__dirname3, "web"), (0, import_node_path16.join)((0, import_node_path16.dirname)(process.execPath), "web"), (0, import_node_path16.join)(__dirname3, "..", "web")].find(
|
|
55675
|
-
(p) => (0, import_node_fs17.existsSync)(p)
|
|
55676
|
-
) ?? (0, import_node_path16.join)(__dirname3, "..", "web");
|
|
55677
|
-
var PORT = Number(process.env.PORT ?? 4317);
|
|
55678
|
-
var HOST = "127.0.0.1";
|
|
55679
|
-
function extractReportHtml(events) {
|
|
55680
|
-
let found = null;
|
|
55681
|
-
for (const ev of events) {
|
|
55682
|
-
const data = ev?.data;
|
|
55683
|
-
if (!data || typeof data !== "object") continue;
|
|
55684
|
-
const result = data.result;
|
|
55685
|
-
const html = typeof data.html === "string" ? data.html : result && typeof result.html === "string" ? result.html : null;
|
|
55686
|
-
if (html) found = { nodeId: ev.node ?? null, html };
|
|
55687
|
-
}
|
|
55688
|
-
return found;
|
|
55689
|
-
}
|
|
55690
|
-
var crashHandlersInstalled = false;
|
|
55691
|
-
function installCrashHandlers() {
|
|
55692
|
-
if (crashHandlersInstalled) return;
|
|
55693
|
-
crashHandlersInstalled = true;
|
|
55694
|
-
ensureLogDir();
|
|
55695
|
-
rotateLog();
|
|
55696
|
-
const record = (kind, err) => {
|
|
55697
|
-
const stack = err instanceof Error && err.stack ? err.stack : String(err);
|
|
55698
|
-
const line = `
|
|
55699
|
-
[${(/* @__PURE__ */ new Date()).toISOString()}] ${kind}
|
|
55700
|
-
${stack}
|
|
55701
|
-
`;
|
|
55702
|
-
try {
|
|
55703
|
-
(0, import_node_fs17.appendFileSync)(logFilePath(), line);
|
|
55704
|
-
} catch {
|
|
55705
|
-
}
|
|
55706
|
-
if (process.stderr.isTTY) process.stderr.write(line);
|
|
55707
|
-
};
|
|
55708
|
-
process.on("uncaughtException", (err) => {
|
|
55709
|
-
record("uncaughtException", err);
|
|
55710
|
-
process.exit(1);
|
|
55711
|
-
});
|
|
55712
|
-
process.on("unhandledRejection", (reason) => {
|
|
55713
|
-
record("unhandledRejection", reason);
|
|
55714
|
-
process.exit(1);
|
|
55715
|
-
});
|
|
55716
|
-
}
|
|
55717
|
-
async function startServer() {
|
|
55718
|
-
installCrashHandlers();
|
|
55719
|
-
const logger = true ? true : { transport: { target: "pino-pretty" } };
|
|
55720
|
-
const app = (0, import_fastify.default)({ logger });
|
|
55721
|
-
app.addContentTypeParser("application/json", { parseAs: "string" }, (_req, body, done) => {
|
|
55722
|
-
const s = body ?? "";
|
|
55723
|
-
if (s.trim() === "") {
|
|
55724
|
-
done(null, {});
|
|
55725
|
-
return;
|
|
55726
|
-
}
|
|
55727
|
-
try {
|
|
55728
|
-
done(null, JSON.parse(s));
|
|
55729
|
-
} catch (err) {
|
|
55730
|
-
done(err);
|
|
55731
|
-
}
|
|
55732
|
-
});
|
|
55733
|
-
await app.register(import_static.default, { root: WEB_ROOT, prefix: "/" });
|
|
55734
|
-
const MIN_AWARE = "0.51.0";
|
|
55735
|
-
const isWin3 = process.platform === "win32";
|
|
55736
|
-
const has = (cmd) => {
|
|
55737
|
-
try {
|
|
55738
|
-
(0, import_node_child_process5.execFileSync)(isWin3 ? "where" : "which", [cmd], { stdio: "ignore" });
|
|
55739
|
-
return true;
|
|
55740
|
-
} catch {
|
|
55741
|
-
return false;
|
|
55742
|
-
}
|
|
55743
|
-
};
|
|
55744
|
-
function installAwareGlobal(spec, onLine = () => {
|
|
55745
|
-
}) {
|
|
55746
|
-
return new Promise((resolve4, reject) => {
|
|
55747
|
-
const child = (0, import_node_child_process5.spawn)("npm", ["i", "-g", `@aware-aeco/cli@${spec}`], { shell: isWin3, windowsHide: true });
|
|
55748
|
-
let stderrTail = "";
|
|
55749
|
-
let combinedTail = "";
|
|
55750
|
-
const tail = (buf, s) => (buf + s).slice(-4e3);
|
|
55751
|
-
child.stdout.on("data", (d) => {
|
|
55752
|
-
const s = d.toString();
|
|
55753
|
-
combinedTail = tail(combinedTail, s);
|
|
55754
|
-
onLine(s.trimEnd());
|
|
55755
|
-
});
|
|
55756
|
-
child.stderr.on("data", (d) => {
|
|
55757
|
-
const s = d.toString();
|
|
55758
|
-
stderrTail = tail(stderrTail, s);
|
|
55759
|
-
combinedTail = tail(combinedTail, s);
|
|
55760
|
-
onLine(s.trimEnd());
|
|
55761
|
-
});
|
|
55762
|
-
child.on("error", (e) => reject(e));
|
|
55763
|
-
child.on("close", (code) => code === 0 ? resolve4() : reject(new Error(`npm exited ${code}: ${stderrTail || combinedTail}`)));
|
|
55764
|
-
});
|
|
55765
|
-
}
|
|
55766
|
-
const bootstrapDeps = {
|
|
55767
|
-
probeToolchain: () => ({ hasNode: has("node"), hasNpm: has("npm") }),
|
|
55768
|
-
getAware: () => {
|
|
55769
|
-
const v = aware.npmVersion();
|
|
55770
|
-
return { installed: v !== null, version: v };
|
|
55771
|
-
},
|
|
55772
|
-
installAware: installAwareGlobal,
|
|
55773
|
-
smoke: () => aware.smoke(),
|
|
55774
|
-
refreshInvoker,
|
|
55775
|
-
emit: broadcast,
|
|
55776
|
-
floor: MIN_AWARE,
|
|
55777
|
-
skip: process.env.AWARE_SKIP_BOOTSTRAP === "1"
|
|
55778
|
-
};
|
|
55779
|
-
app.setErrorHandler((err, _req, reply) => {
|
|
55780
|
-
if (err instanceof AppNotFoundError) {
|
|
55781
|
-
return reply.status(404).send({ ok: false, error: err.message });
|
|
55782
|
-
}
|
|
55783
|
-
if (err instanceof RoutineError) {
|
|
55784
|
-
return reply.status(err.status).send({ ok: false, error: err.message });
|
|
55785
|
-
}
|
|
55786
|
-
if (err instanceof AwareError) {
|
|
55787
|
-
app.log.warn({ detail: err.detail }, err.message);
|
|
55788
|
-
return reply.status(502).send({ ok: false, error: err.message, detail: err.detail ?? null });
|
|
55789
|
-
}
|
|
55790
|
-
app.log.error(err);
|
|
55791
|
-
return reply.status(500).send({ ok: false, error: err.message });
|
|
55792
|
-
});
|
|
55793
|
-
const SELF_ORIGINS = /* @__PURE__ */ new Set([
|
|
55794
|
-
`http://127.0.0.1:${PORT}`,
|
|
55795
|
-
`http://localhost:${PORT}`,
|
|
55796
|
-
`http://floless.localhost:${PORT}`
|
|
55797
|
-
// branded address (browsers resolve *.localhost → 127.0.0.1)
|
|
55798
|
-
]);
|
|
55799
|
-
app.addHook("onRequest", async (req, reply) => {
|
|
55800
|
-
if (!req.url.startsWith("/api/")) return;
|
|
55801
|
-
const origin = req.headers.origin;
|
|
55802
|
-
if (origin && !SELF_ORIGINS.has(origin)) {
|
|
55803
|
-
return reply.status(403).send({ ok: false, error: "cross-origin request rejected" });
|
|
55804
|
-
}
|
|
55805
|
-
});
|
|
55806
|
-
app.addHook("onRequest", async (req, reply) => {
|
|
55807
|
-
if (!req.url.startsWith("/api/")) return;
|
|
55808
|
-
if (req.url.startsWith("/api/health") || req.url.startsWith("/api/license/") || req.url.startsWith("/api/bootstrap/") || req.url.startsWith("/api/autostart") || req.url.startsWith("/api/update") || req.url.startsWith("/api/aware/update")) return;
|
|
55809
|
-
const { state: state2, signInUrl: signInUrl2 } = await getLicenseStatus();
|
|
55810
|
-
if (state2 !== "valid" && state2 !== "offline-grace") {
|
|
55811
|
-
return reply.status(402).send({ ok: false, error: "subscription required", state: state2, signInUrl: signInUrl2 });
|
|
55812
|
-
}
|
|
55813
|
-
});
|
|
55814
|
-
app.addHook("onRequest", async (req, reply) => {
|
|
55815
|
-
if (!isGatedAwareRoute(req.url, req.method)) return;
|
|
55816
|
-
const st = getBootstrapState();
|
|
55817
|
-
if (st.status === "ready") return;
|
|
55818
|
-
if (st.status === "failed") return reply.status(503).send({ ok: false, error: "AWARE unavailable", bootstrap: st.status, reason: st.reason, remediation: st.remediation });
|
|
55819
|
-
return reply.status(409).send({ ok: false, error: "AWARE is still setting up", bootstrap: st.status });
|
|
55820
|
-
});
|
|
55821
|
-
app.get("/api/health", async () => {
|
|
55822
|
-
const bs = getBootstrapState();
|
|
55823
|
-
return {
|
|
55824
|
-
ok: true,
|
|
55825
|
-
appVersion: appVersion(),
|
|
55826
|
-
// the installed build (sq.version), so it's scriptable
|
|
55827
|
-
awareVersion: awareInstalledVersion() ?? bs.awareVersion,
|
|
55828
|
-
// fresh (TTL-cached) install version; the boot snapshot is only a fallback
|
|
55829
|
-
awareReady: bs.awareReady,
|
|
55830
|
-
bootstrap: bs.status,
|
|
55831
|
-
bootstrapReason: bs.reason,
|
|
55832
|
-
bootstrapRemediation: bs.remediation,
|
|
55833
|
-
// UI maps reason→fixed copy; this is secondary dev context
|
|
55834
|
-
sseClients: clientCount(),
|
|
55835
|
-
license: (await getLicenseStatus()).state
|
|
55836
|
-
// UI + launcher branch on this
|
|
55837
|
-
};
|
|
55838
|
-
});
|
|
55839
|
-
app.get("/api/license/status", async () => ({ ok: true, ...await getLicenseStatus() }));
|
|
55840
|
-
app.post("/api/license/start", async () => ({ ok: true, ...await startLogin() }));
|
|
55841
|
-
app.post("/api/license/logout", async () => {
|
|
55842
|
-
logout();
|
|
55843
|
-
return { ok: true };
|
|
55844
|
-
});
|
|
55845
|
-
app.get("/api/autostart", async () => {
|
|
55846
|
-
const supported = autostartSupported();
|
|
55847
|
-
return { ok: true, supported, enabled: supported ? autostartPresent() : false };
|
|
55848
|
-
});
|
|
55849
|
-
app.put("/api/autostart", async (req, reply) => {
|
|
55850
|
-
if (!autostartSupported()) return reply.send({ ok: true, supported: false, enabled: false });
|
|
55851
|
-
if (typeof req.body?.enabled !== "boolean") {
|
|
55852
|
-
return reply.status(400).send({ ok: false, error: "body { enabled: boolean } required" });
|
|
55853
|
-
}
|
|
55854
|
-
try {
|
|
55855
|
-
if (req.body.enabled) registerAutostart(resolveRealInstallExe(process.execPath));
|
|
55856
|
-
else unregisterAutostart();
|
|
55857
|
-
} catch (err) {
|
|
55858
|
-
return reply.status(500).send({ ok: false, error: err instanceof Error ? err.message : String(err) });
|
|
55859
|
-
}
|
|
55860
|
-
return { ok: true, supported: true, enabled: autostartPresent() };
|
|
55861
|
-
});
|
|
55862
|
-
app.get("/api/update", async () => {
|
|
55863
|
-
return { ok: true, ...await checkForUpdate() };
|
|
55864
|
-
});
|
|
55865
|
-
app.post("/api/update/apply", async (_req, reply) => {
|
|
55866
|
-
const check = await checkForUpdate();
|
|
55867
|
-
if (!check.supported || !check.updateAvailable) {
|
|
55868
|
-
return reply.status(409).send({ ok: false, ...check, error: check.reason ?? "no update available" });
|
|
55869
|
-
}
|
|
55870
|
-
const result = await applyUpdate(check);
|
|
55871
|
-
if (!result.applied) return reply.status(500).send({ ok: false, error: result.message });
|
|
55872
|
-
reply.send({ ok: true, ...result });
|
|
55873
|
-
setTimeout(() => process.exit(0), 250);
|
|
55874
|
-
});
|
|
55875
|
-
app.get("/api/aware/update", async () => {
|
|
55876
|
-
return { ok: true, ...await checkAwareUpdate() };
|
|
55877
|
-
});
|
|
55878
|
-
let awareInstallInFlight = false;
|
|
55879
|
-
app.post("/api/aware/update/apply", async (_req, reply) => {
|
|
55880
|
-
const block = awareUpgradeBlockReason({
|
|
55881
|
-
bootstrapInstalling: getBootstrapState().status === "installing",
|
|
55882
|
-
installInFlight: awareInstallInFlight,
|
|
55883
|
-
runActive: isRunActive(),
|
|
55884
|
-
hostWatcherActive: hostWatcherActive()
|
|
56023
|
+
stream.once(STR_END, () => {
|
|
56024
|
+
if (stream) {
|
|
56025
|
+
this._streams.delete(stream);
|
|
56026
|
+
stream = void 0;
|
|
56027
|
+
}
|
|
55885
56028
|
});
|
|
55886
|
-
|
|
55887
|
-
|
|
56029
|
+
return stream;
|
|
56030
|
+
}
|
|
56031
|
+
};
|
|
56032
|
+
function watch(paths, options = {}) {
|
|
56033
|
+
const watcher = new FSWatcher(options);
|
|
56034
|
+
watcher.add(paths);
|
|
56035
|
+
return watcher;
|
|
56036
|
+
}
|
|
56037
|
+
var esm_default = { watch, FSWatcher };
|
|
56038
|
+
|
|
56039
|
+
// watch.ts
|
|
56040
|
+
function appIdFromLogPath(path) {
|
|
56041
|
+
const parts = path.split(/[\\/]/);
|
|
56042
|
+
const i = parts.lastIndexOf("logs");
|
|
56043
|
+
return i >= 0 && parts[i + 1] ? parts[i + 1] : null;
|
|
56044
|
+
}
|
|
56045
|
+
function startWatcher() {
|
|
56046
|
+
const awareDir = process.env.AWARE_HOME ?? (0, import_node_path16.join)((0, import_node_os10.homedir)(), ".aware");
|
|
56047
|
+
const credentialsDir = (0, import_node_path16.join)(awareDir, "credentials");
|
|
56048
|
+
if (!(0, import_node_fs17.existsSync)(credentialsDir)) {
|
|
55888
56049
|
try {
|
|
55889
|
-
|
|
55890
|
-
|
|
55891
|
-
return { ok: true, version: res.version };
|
|
55892
|
-
} finally {
|
|
55893
|
-
awareInstallInFlight = false;
|
|
55894
|
-
}
|
|
55895
|
-
});
|
|
55896
|
-
app.post("/api/bootstrap/retry", async () => {
|
|
55897
|
-
const st = getBootstrapState().status;
|
|
55898
|
-
if (st === "failed" || st === "idle") {
|
|
55899
|
-
void runBootstrap(bootstrapDeps);
|
|
56050
|
+
(0, import_node_fs17.mkdirSync)(credentialsDir, { recursive: true });
|
|
56051
|
+
} catch {
|
|
55900
56052
|
}
|
|
55901
|
-
|
|
55902
|
-
|
|
55903
|
-
|
|
55904
|
-
|
|
55905
|
-
|
|
55906
|
-
|
|
55907
|
-
|
|
55908
|
-
|
|
55909
|
-
const agents = await aware.agentList();
|
|
55910
|
-
return { ok: true, agents };
|
|
55911
|
-
});
|
|
55912
|
-
app.get("/api/agent/:id", async (req) => {
|
|
55913
|
-
const agent = await aware.agentDescribe(req.params.id);
|
|
55914
|
-
return { ok: true, agent };
|
|
56053
|
+
}
|
|
56054
|
+
const targets = ["apps", "logs", "credentials"].map((d) => (0, import_node_path16.join)(awareDir, d)).filter((p) => (0, import_node_fs17.existsSync)(p));
|
|
56055
|
+
if (targets.length === 0) {
|
|
56056
|
+
return null;
|
|
56057
|
+
}
|
|
56058
|
+
const watcher = esm_default.watch(targets, {
|
|
56059
|
+
ignoreInitial: true,
|
|
56060
|
+
awaitWriteFinish: { stabilityThreshold: 150, pollInterval: 50 }
|
|
55915
56061
|
});
|
|
55916
|
-
|
|
55917
|
-
const
|
|
55918
|
-
|
|
55919
|
-
|
|
55920
|
-
|
|
55921
|
-
|
|
55922
|
-
|
|
55923
|
-
|
|
55924
|
-
|
|
55925
|
-
|
|
56062
|
+
watcher.on("all", (event, path) => {
|
|
56063
|
+
const isCredential = path.split(/[\\/]/).includes("credentials");
|
|
56064
|
+
const kind = isCredential ? "credential" : path.endsWith(".jsonl") ? "trace" : path.endsWith(".lock") ? "lock" : path.endsWith(".flo") || path.endsWith(".app") ? "source" : "file";
|
|
56065
|
+
broadcast({ type: "fs-change", kind, event, path });
|
|
56066
|
+
if (kind === "trace" && event !== "unlink" && (0, import_node_fs17.existsSync)(path)) {
|
|
56067
|
+
const id = appIdFromLogPath(path);
|
|
56068
|
+
if (!id) return;
|
|
56069
|
+
try {
|
|
56070
|
+
broadcast({ type: "trace-file", id, runId: path.split(import_node_path16.sep).pop()?.replace(/\.jsonl$/, "") ?? null, events: parseTrace((0, import_node_fs17.readFileSync)(path, "utf8")) });
|
|
56071
|
+
} catch {
|
|
55926
56072
|
}
|
|
55927
|
-
} catch (err) {
|
|
55928
|
-
app.log.warn({ err: String(err) }, "aware connect --list unavailable; using file-based status");
|
|
55929
56073
|
}
|
|
55930
|
-
return { ok: true, integrations };
|
|
55931
56074
|
});
|
|
55932
|
-
|
|
55933
|
-
|
|
55934
|
-
|
|
55935
|
-
|
|
55936
|
-
|
|
55937
|
-
|
|
55938
|
-
|
|
55939
|
-
|
|
55940
|
-
|
|
55941
|
-
|
|
55942
|
-
|
|
55943
|
-
|
|
56075
|
+
return { close: () => watcher.close() };
|
|
56076
|
+
}
|
|
56077
|
+
|
|
56078
|
+
// index.ts
|
|
56079
|
+
var __dirname4 = (0, import_node_path17.dirname)((0, import_node_url3.fileURLToPath)(__import_meta_url));
|
|
56080
|
+
var WEB_ROOT = [(0, import_node_path17.join)(__dirname4, "web"), (0, import_node_path17.join)((0, import_node_path17.dirname)(process.execPath), "web"), (0, import_node_path17.join)(__dirname4, "..", "web")].find(
|
|
56081
|
+
(p) => (0, import_node_fs18.existsSync)(p)
|
|
56082
|
+
) ?? (0, import_node_path17.join)(__dirname4, "..", "web");
|
|
56083
|
+
var PORT2 = Number(process.env.PORT ?? 4317);
|
|
56084
|
+
var HOST = "127.0.0.1";
|
|
56085
|
+
function extractReportHtml(events) {
|
|
56086
|
+
let found = null;
|
|
56087
|
+
for (const ev of events) {
|
|
56088
|
+
const data = ev?.data;
|
|
56089
|
+
if (!data || typeof data !== "object") continue;
|
|
56090
|
+
const result = data.result;
|
|
56091
|
+
const html = typeof data.html === "string" ? data.html : result && typeof result.html === "string" ? result.html : null;
|
|
56092
|
+
if (html) found = { nodeId: ev.node ?? null, html };
|
|
56093
|
+
}
|
|
56094
|
+
return found;
|
|
56095
|
+
}
|
|
56096
|
+
var crashHandlersInstalled = false;
|
|
56097
|
+
function installCrashHandlers() {
|
|
56098
|
+
if (crashHandlersInstalled) return;
|
|
56099
|
+
crashHandlersInstalled = true;
|
|
56100
|
+
ensureLogDir();
|
|
56101
|
+
rotateLog();
|
|
56102
|
+
const record = (kind, err) => {
|
|
56103
|
+
const stack = err instanceof Error && err.stack ? err.stack : String(err);
|
|
56104
|
+
const line = `
|
|
56105
|
+
[${(/* @__PURE__ */ new Date()).toISOString()}] ${kind}
|
|
56106
|
+
${stack}
|
|
56107
|
+
`;
|
|
56108
|
+
try {
|
|
56109
|
+
(0, import_node_fs18.appendFileSync)(logFilePath(), line);
|
|
56110
|
+
} catch {
|
|
55944
56111
|
}
|
|
55945
|
-
|
|
55946
|
-
|
|
55947
|
-
|
|
55948
|
-
|
|
55949
|
-
|
|
55950
|
-
error: result.status === "connected" ? null : "sign-in failed or was cancelled"
|
|
55951
|
-
});
|
|
55952
|
-
});
|
|
55953
|
-
return { ok: true, id, flow, started: true };
|
|
55954
|
-
});
|
|
55955
|
-
app.get("/api/app/:id", async (req) => {
|
|
55956
|
-
return { ok: true, app: readApp(req.params.id) };
|
|
56112
|
+
if (process.stderr.isTTY) process.stderr.write(line);
|
|
56113
|
+
};
|
|
56114
|
+
process.on("uncaughtException", (err) => {
|
|
56115
|
+
record("uncaughtException", err);
|
|
56116
|
+
process.exit(1);
|
|
55957
56117
|
});
|
|
55958
|
-
|
|
55959
|
-
|
|
55960
|
-
|
|
55961
|
-
if (!path) return reply.status(400).send({ ok: false, error: "id or sourcePath required" });
|
|
55962
|
-
const result = await aware.compile(path);
|
|
55963
|
-
broadcast({ type: "compiled", id: id ?? null, lockPath: result.lockPath });
|
|
55964
|
-
return { ok: true, result };
|
|
56118
|
+
process.on("unhandledRejection", (reason) => {
|
|
56119
|
+
record("unhandledRejection", reason);
|
|
56120
|
+
process.exit(1);
|
|
55965
56121
|
});
|
|
55966
|
-
|
|
55967
|
-
|
|
55968
|
-
|
|
55969
|
-
|
|
55970
|
-
|
|
55971
|
-
|
|
56122
|
+
}
|
|
56123
|
+
async function startServer() {
|
|
56124
|
+
installCrashHandlers();
|
|
56125
|
+
const logger = true ? true : { transport: { target: "pino-pretty" } };
|
|
56126
|
+
const app = (0, import_fastify.default)({ logger });
|
|
56127
|
+
app.addContentTypeParser("application/json", { parseAs: "string" }, (_req, body, done) => {
|
|
56128
|
+
const s = body ?? "";
|
|
56129
|
+
if (s.trim() === "") {
|
|
56130
|
+
done(null, {});
|
|
56131
|
+
return;
|
|
55972
56132
|
}
|
|
55973
|
-
const inputs = appData.inputs.map((i) => ({ name: i.name, type: i.type }));
|
|
55974
|
-
const baked = bakeFloSource(appData.source.text, inputs);
|
|
55975
|
-
const tmpRoot = (0, import_node_fs17.mkdtempSync)((0, import_node_path16.join)((0, import_node_os11.tmpdir)(), "floless-bake-"));
|
|
55976
|
-
const backupDir = (0, import_node_path16.join)(tmpRoot, `${id}-backup`);
|
|
55977
|
-
const bakeDir = (0, import_node_path16.join)(tmpRoot, id);
|
|
55978
|
-
(0, import_node_fs17.cpSync)(appDir(id), backupDir, { recursive: true });
|
|
55979
|
-
(0, import_node_fs17.cpSync)(appDir(id), bakeDir, { recursive: true });
|
|
55980
|
-
const floName = appData.source.path.split(/[\\/]/).pop();
|
|
55981
|
-
(0, import_node_fs17.writeFileSync)((0, import_node_path16.join)(bakeDir, floName), baked);
|
|
55982
|
-
let appInstalled = true;
|
|
55983
56133
|
try {
|
|
55984
|
-
|
|
55985
|
-
|
|
55986
|
-
|
|
55987
|
-
await aware.install(bakeDir);
|
|
55988
|
-
appInstalled = true;
|
|
55989
|
-
} catch (installErr) {
|
|
55990
|
-
try {
|
|
55991
|
-
await aware.install(backupDir);
|
|
55992
|
-
appInstalled = true;
|
|
55993
|
-
} catch (restoreErr) {
|
|
55994
|
-
app.log.error({ id, tmpRoot, restoreErr: String(restoreErr) }, "bake: install AND restore failed \u2014 app uninstalled; backup retained");
|
|
55995
|
-
return reply.status(502).send({
|
|
55996
|
-
ok: false,
|
|
55997
|
-
error: `bake failed and the app could not be restored \u2014 its source is backed up at ${backupDir}. Reinstall it from there with: aware app install "${backupDir}"`
|
|
55998
|
-
});
|
|
55999
|
-
}
|
|
56000
|
-
throw installErr;
|
|
56001
|
-
}
|
|
56002
|
-
try {
|
|
56003
|
-
await aware.compile((0, import_node_path16.join)(appDir(id), floName));
|
|
56004
|
-
} catch (compileErr) {
|
|
56005
|
-
app.log.warn({ id, compileErr: String(compileErr) }, "bake: post-install recompile failed (app baked but may need a manual Compile)");
|
|
56006
|
-
}
|
|
56007
|
-
broadcast({ type: "baked", id });
|
|
56008
|
-
return { ok: true, id, agent: id, inputs };
|
|
56009
|
-
} finally {
|
|
56010
|
-
if (appInstalled) (0, import_node_fs17.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
56134
|
+
done(null, JSON.parse(s));
|
|
56135
|
+
} catch (err) {
|
|
56136
|
+
done(err);
|
|
56011
56137
|
}
|
|
56012
56138
|
});
|
|
56013
|
-
|
|
56014
|
-
|
|
56015
|
-
|
|
56016
|
-
|
|
56017
|
-
let safe;
|
|
56139
|
+
await app.register(import_static.default, { root: WEB_ROOT, prefix: "/" });
|
|
56140
|
+
const MIN_AWARE = "0.51.0";
|
|
56141
|
+
const isWin3 = process.platform === "win32";
|
|
56142
|
+
const has = (cmd) => {
|
|
56018
56143
|
try {
|
|
56019
|
-
|
|
56020
|
-
|
|
56021
|
-
|
|
56022
|
-
|
|
56023
|
-
return { ok: true, files: matchAssemblies(safe) };
|
|
56024
|
-
});
|
|
56025
|
-
app.post("/api/graft/introspect", async (req, reply) => {
|
|
56026
|
-
const { sourceKind, sourceRef, decompile, outputId, acceptLicense, referenceDirs } = req.body ?? {};
|
|
56027
|
-
if (!sourceKind || !sourceRef) {
|
|
56028
|
-
return reply.status(400).send({ ok: false, error: "sourceKind and sourceRef required" });
|
|
56144
|
+
(0, import_node_child_process6.execFileSync)(isWin3 ? "where" : "which", [cmd], { stdio: "ignore", windowsHide: true, timeout: 5e3 });
|
|
56145
|
+
return true;
|
|
56146
|
+
} catch {
|
|
56147
|
+
return false;
|
|
56029
56148
|
}
|
|
56030
|
-
|
|
56031
|
-
|
|
56032
|
-
|
|
56033
|
-
|
|
56034
|
-
|
|
56035
|
-
|
|
56036
|
-
|
|
56037
|
-
|
|
56038
|
-
|
|
56039
|
-
|
|
56040
|
-
|
|
56149
|
+
};
|
|
56150
|
+
function installAwareGlobal(spec, onLine = () => {
|
|
56151
|
+
}) {
|
|
56152
|
+
return new Promise((resolve4, reject) => {
|
|
56153
|
+
const child = (0, import_node_child_process6.spawn)("npm", ["i", "-g", `@aware-aeco/cli@${spec}`], { shell: isWin3, windowsHide: true });
|
|
56154
|
+
let stderrTail = "";
|
|
56155
|
+
let combinedTail = "";
|
|
56156
|
+
const tail = (buf, s) => (buf + s).slice(-4e3);
|
|
56157
|
+
child.stdout.on("data", (d) => {
|
|
56158
|
+
const s = d.toString();
|
|
56159
|
+
combinedTail = tail(combinedTail, s);
|
|
56160
|
+
onLine(s.trimEnd());
|
|
56041
56161
|
});
|
|
56042
|
-
|
|
56043
|
-
|
|
56044
|
-
|
|
56045
|
-
|
|
56162
|
+
child.stderr.on("data", (d) => {
|
|
56163
|
+
const s = d.toString();
|
|
56164
|
+
stderrTail = tail(stderrTail, s);
|
|
56165
|
+
combinedTail = tail(combinedTail, s);
|
|
56166
|
+
onLine(s.trimEnd());
|
|
56167
|
+
});
|
|
56168
|
+
child.on("error", (e) => reject(e));
|
|
56169
|
+
child.on("close", (code) => code === 0 ? resolve4() : reject(new Error(`npm exited ${code}: ${stderrTail || combinedTail}`)));
|
|
56170
|
+
});
|
|
56171
|
+
}
|
|
56172
|
+
const bootstrapDeps = {
|
|
56173
|
+
probeToolchain: () => ({ hasNode: has("node"), hasNpm: has("npm") }),
|
|
56174
|
+
getAware: () => {
|
|
56175
|
+
const v = aware.npmVersion();
|
|
56176
|
+
return { installed: v !== null, version: v };
|
|
56177
|
+
},
|
|
56178
|
+
installAware: installAwareGlobal,
|
|
56179
|
+
smoke: () => aware.smoke(),
|
|
56180
|
+
refreshInvoker,
|
|
56181
|
+
emit: broadcast,
|
|
56182
|
+
floor: MIN_AWARE,
|
|
56183
|
+
skip: process.env.AWARE_SKIP_BOOTSTRAP === "1"
|
|
56184
|
+
};
|
|
56185
|
+
app.setErrorHandler((err, _req, reply) => {
|
|
56186
|
+
if (err instanceof AppNotFoundError) {
|
|
56187
|
+
return reply.status(404).send({ ok: false, error: err.message });
|
|
56046
56188
|
}
|
|
56047
|
-
|
|
56048
|
-
|
|
56049
|
-
(0, import_node_fs17.rmSync)(tempHome, { recursive: true, force: true });
|
|
56050
|
-
return reply.status(502).send({ ok: false, error: `build produced output at ${result.agentDir} but no manifest.yaml` });
|
|
56189
|
+
if (err instanceof RoutineError) {
|
|
56190
|
+
return reply.status(err.status).send({ ok: false, error: err.message });
|
|
56051
56191
|
}
|
|
56052
|
-
|
|
56053
|
-
|
|
56054
|
-
|
|
56055
|
-
if ((0, import_node_fs17.existsSync)((0, import_node_path16.join)(graftAgentsDir(), result.agentId))) {
|
|
56056
|
-
preview.warnings.unshift(`An agent named "${result.agentId}" is already installed \u2014 creating it will overwrite it.`);
|
|
56192
|
+
if (err instanceof AwareError) {
|
|
56193
|
+
app.log.warn({ detail: err.detail }, err.message);
|
|
56194
|
+
return reply.status(502).send({ ok: false, error: err.message, detail: err.detail ?? null });
|
|
56057
56195
|
}
|
|
56058
|
-
|
|
56196
|
+
app.log.error(err);
|
|
56197
|
+
return reply.status(500).send({ ok: false, error: err.message });
|
|
56059
56198
|
});
|
|
56060
|
-
|
|
56061
|
-
|
|
56062
|
-
|
|
56063
|
-
|
|
56064
|
-
|
|
56065
|
-
|
|
56066
|
-
|
|
56067
|
-
|
|
56068
|
-
|
|
56069
|
-
|
|
56070
|
-
|
|
56071
|
-
registerStage(stagedRef, stage.tempDir, stage.agentId);
|
|
56072
|
-
return reply.status(409).send({ ok: false, error: err.message, agentId: stage.agentId, collision: true });
|
|
56073
|
-
}
|
|
56074
|
-
(0, import_node_fs17.rmSync)(stage.tempDir, { recursive: true, force: true });
|
|
56075
|
-
throw err;
|
|
56199
|
+
const SELF_ORIGINS = /* @__PURE__ */ new Set([
|
|
56200
|
+
`http://127.0.0.1:${PORT2}`,
|
|
56201
|
+
`http://localhost:${PORT2}`,
|
|
56202
|
+
`http://floless.localhost:${PORT2}`
|
|
56203
|
+
// branded address (browsers resolve *.localhost → 127.0.0.1)
|
|
56204
|
+
]);
|
|
56205
|
+
app.addHook("onRequest", async (req, reply) => {
|
|
56206
|
+
if (!req.url.startsWith("/api/")) return;
|
|
56207
|
+
const origin = req.headers.origin;
|
|
56208
|
+
if (origin && !SELF_ORIGINS.has(origin)) {
|
|
56209
|
+
return reply.status(403).send({ ok: false, error: "cross-origin request rejected" });
|
|
56076
56210
|
}
|
|
56077
|
-
broadcast({ type: "grafted", id: stage.agentId });
|
|
56078
|
-
return { ok: true, agentId: stage.agentId };
|
|
56079
56211
|
});
|
|
56080
|
-
app.
|
|
56081
|
-
"/api/
|
|
56082
|
-
|
|
56083
|
-
|
|
56084
|
-
|
|
56085
|
-
|
|
56086
|
-
if (blocked) return reply.send({ ok: false, error: blocked, blocked: true, simulate: !!simulate });
|
|
56087
|
-
let result;
|
|
56088
|
-
try {
|
|
56089
|
-
result = await aware.run(id, { dryRun, simulate, inputs });
|
|
56090
|
-
} catch (err) {
|
|
56091
|
-
if (err instanceof AwareError) {
|
|
56092
|
-
const detail = err.detail && typeof err.detail === "object" ? err.detail : null;
|
|
56093
|
-
if (detail && detail.cancelled === true) {
|
|
56094
|
-
app.log.info({ id }, "run cancelled by user");
|
|
56095
|
-
return reply.send({ ok: false, error: "Run cancelled", cancelled: true, simulate: !!simulate });
|
|
56096
|
-
}
|
|
56097
|
-
const stderr = detail && typeof detail.stderr === "string" ? detail.stderr.trim() : "";
|
|
56098
|
-
app.log.warn({ detail: err.detail }, err.message);
|
|
56099
|
-
return reply.send({ ok: false, error: err.message, reason: stderr || null, simulate: !!simulate });
|
|
56100
|
-
}
|
|
56101
|
-
throw err;
|
|
56102
|
-
}
|
|
56103
|
-
const events = result.traceText ? parseTrace(result.traceText) : [];
|
|
56104
|
-
broadcast({ type: "run-started", id, dryRun: !!dryRun, simulate: !!simulate, runId: result.runId });
|
|
56105
|
-
for (const ev of events) broadcast({ type: "trace", id, event: ev });
|
|
56106
|
-
broadcast({ type: "run-ended", id, runId: result.runId });
|
|
56107
|
-
const report = extractReportHtml(events);
|
|
56108
|
-
return { ok: true, runId: result.runId, tracePath: result.tracePath, events, report };
|
|
56212
|
+
app.addHook("onRequest", async (req, reply) => {
|
|
56213
|
+
if (!req.url.startsWith("/api/")) return;
|
|
56214
|
+
if (req.url.startsWith("/api/health") || req.url.startsWith("/api/license/") || req.url.startsWith("/api/bootstrap/") || req.url.startsWith("/api/autostart") || req.url.startsWith("/api/update") || req.url.startsWith("/api/aware/update")) return;
|
|
56215
|
+
const { state: state2, signInUrl: signInUrl2 } = await getLicenseStatus();
|
|
56216
|
+
if (state2 !== "valid" && state2 !== "offline-grace") {
|
|
56217
|
+
return reply.status(402).send({ ok: false, error: "subscription required", state: state2, signInUrl: signInUrl2 });
|
|
56109
56218
|
}
|
|
56110
|
-
);
|
|
56111
|
-
app.post("/api/run/stop", async () => {
|
|
56112
|
-
const running = cancelActiveRun();
|
|
56113
|
-
return { ok: true, running };
|
|
56114
56219
|
});
|
|
56115
|
-
|
|
56116
|
-
|
|
56117
|
-
|
|
56118
|
-
|
|
56119
|
-
|
|
56120
|
-
|
|
56121
|
-
|
|
56122
|
-
|
|
56123
|
-
|
|
56124
|
-
|
|
56125
|
-
|
|
56126
|
-
|
|
56127
|
-
|
|
56128
|
-
|
|
56129
|
-
|
|
56130
|
-
|
|
56131
|
-
|
|
56132
|
-
|
|
56133
|
-
|
|
56134
|
-
|
|
56135
|
-
|
|
56136
|
-
|
|
56137
|
-
|
|
56138
|
-
|
|
56139
|
-
|
|
56140
|
-
|
|
56141
|
-
|
|
56142
|
-
|
|
56143
|
-
|
|
56144
|
-
|
|
56145
|
-
|
|
56146
|
-
|
|
56147
|
-
|
|
56148
|
-
|
|
56149
|
-
|
|
56150
|
-
|
|
56151
|
-
|
|
56152
|
-
|
|
56153
|
-
|
|
56154
|
-
);
|
|
56155
|
-
app.get("/api/routines", async () => ({
|
|
56156
|
-
// Annotate each with live state so the UI reconciles from the server (a dropped
|
|
56157
|
-
// SSE event can't wedge an indicator): schedule routines carry queued/active
|
|
56158
|
-
// `running`; trigger routines carry their live `session` snapshot
|
|
56159
|
-
// (listening/fired×N/error/stopped).
|
|
56160
|
-
ok: true,
|
|
56161
|
-
routines: listRoutines().map(
|
|
56162
|
-
(r) => r.kind === "trigger" ? { ...r, session: getSnapshot(r.id) } : { ...r, running: isRoutineRunning(r.id) }
|
|
56163
|
-
),
|
|
56164
|
-
max: MAX_ROUTINES
|
|
56165
|
-
}));
|
|
56166
|
-
app.post(
|
|
56167
|
-
"/api/routines",
|
|
56168
|
-
async (req, reply) => {
|
|
56169
|
-
const routine = createRoutine(req.body ?? {});
|
|
56170
|
-
return reply.status(201).send({ ok: true, routine });
|
|
56220
|
+
app.addHook("onRequest", async (req, reply) => {
|
|
56221
|
+
if (!isGatedAwareRoute(req.url, req.method)) return;
|
|
56222
|
+
const st = getBootstrapState();
|
|
56223
|
+
if (st.status === "ready") return;
|
|
56224
|
+
if (st.status === "failed") return reply.status(503).send({ ok: false, error: "AWARE unavailable", bootstrap: st.status, reason: st.reason, remediation: st.remediation });
|
|
56225
|
+
return reply.status(409).send({ ok: false, error: "AWARE is still setting up", bootstrap: st.status });
|
|
56226
|
+
});
|
|
56227
|
+
app.get("/api/health", async () => {
|
|
56228
|
+
const bs = getBootstrapState();
|
|
56229
|
+
return {
|
|
56230
|
+
ok: true,
|
|
56231
|
+
appVersion: appVersion(),
|
|
56232
|
+
// the installed build (sq.version), so it's scriptable
|
|
56233
|
+
awareVersion: awareInstalledVersion() ?? bs.awareVersion,
|
|
56234
|
+
// fresh (TTL-cached) install version; the boot snapshot is only a fallback
|
|
56235
|
+
awareReady: bs.awareReady,
|
|
56236
|
+
bootstrap: bs.status,
|
|
56237
|
+
bootstrapReason: bs.reason,
|
|
56238
|
+
bootstrapRemediation: bs.remediation,
|
|
56239
|
+
// UI maps reason→fixed copy; this is secondary dev context
|
|
56240
|
+
sseClients: clientCount(),
|
|
56241
|
+
license: (await getLicenseStatus()).state
|
|
56242
|
+
// UI + launcher branch on this
|
|
56243
|
+
};
|
|
56244
|
+
});
|
|
56245
|
+
app.get("/api/license/status", async () => ({ ok: true, ...await getLicenseStatus() }));
|
|
56246
|
+
app.post("/api/license/start", async () => ({ ok: true, ...await startLogin() }));
|
|
56247
|
+
app.post("/api/license/logout", async () => {
|
|
56248
|
+
logout();
|
|
56249
|
+
return { ok: true };
|
|
56250
|
+
});
|
|
56251
|
+
app.get("/api/autostart", async () => {
|
|
56252
|
+
const supported = autostartSupported();
|
|
56253
|
+
return { ok: true, supported, enabled: supported ? autostartPresent() : false };
|
|
56254
|
+
});
|
|
56255
|
+
app.put("/api/autostart", async (req, reply) => {
|
|
56256
|
+
if (!autostartSupported()) return reply.send({ ok: true, supported: false, enabled: false });
|
|
56257
|
+
if (typeof req.body?.enabled !== "boolean") {
|
|
56258
|
+
return reply.status(400).send({ ok: false, error: "body { enabled: boolean } required" });
|
|
56171
56259
|
}
|
|
56172
|
-
|
|
56173
|
-
|
|
56174
|
-
|
|
56175
|
-
|
|
56176
|
-
|
|
56177
|
-
if (!routine) return reply.status(404).send({ ok: false, error: "routine not found" });
|
|
56178
|
-
return { ok: true, routine };
|
|
56260
|
+
try {
|
|
56261
|
+
if (req.body.enabled) registerAutostart(resolveRealInstallExe(process.execPath));
|
|
56262
|
+
else unregisterAutostart();
|
|
56263
|
+
} catch (err) {
|
|
56264
|
+
return reply.status(500).send({ ok: false, error: err instanceof Error ? err.message : String(err) });
|
|
56179
56265
|
}
|
|
56180
|
-
|
|
56181
|
-
app.delete("/api/routines/:id", async (req, reply) => {
|
|
56182
|
-
if (!deleteRoutine(req.params.id)) return reply.status(404).send({ ok: false, error: "routine not found" });
|
|
56183
|
-
return { ok: true };
|
|
56266
|
+
return { ok: true, supported: true, enabled: autostartPresent() };
|
|
56184
56267
|
});
|
|
56185
|
-
app.
|
|
56186
|
-
|
|
56187
|
-
return { ok: true };
|
|
56268
|
+
app.get("/api/update", async () => {
|
|
56269
|
+
return { ok: true, ...await checkForUpdate() };
|
|
56188
56270
|
});
|
|
56189
|
-
app.
|
|
56190
|
-
|
|
56191
|
-
|
|
56192
|
-
|
|
56193
|
-
const { name, category, node, source } = req.body ?? {};
|
|
56194
|
-
if (!name || !node) return reply.status(400).send({ ok: false, error: "name and node required" });
|
|
56195
|
-
const tpl = addTemplate({ name, category, node, source });
|
|
56196
|
-
broadcast({ type: "templates-changed" });
|
|
56197
|
-
return { ok: true, template: tpl };
|
|
56271
|
+
app.post("/api/update/apply", async (_req, reply) => {
|
|
56272
|
+
const check = await checkForUpdate();
|
|
56273
|
+
if (!check.supported || !check.updateAvailable) {
|
|
56274
|
+
return reply.status(409).send({ ok: false, ...check, error: check.reason ?? "no update available" });
|
|
56198
56275
|
}
|
|
56199
|
-
|
|
56200
|
-
|
|
56201
|
-
|
|
56202
|
-
|
|
56203
|
-
|
|
56276
|
+
const result = await applyUpdate(check, { onBeforeApply: async () => {
|
|
56277
|
+
killSupervisor({ tree: false });
|
|
56278
|
+
} });
|
|
56279
|
+
if (!result.applied) return reply.status(500).send({ ok: false, error: result.message });
|
|
56280
|
+
reply.send({ ok: true, ...result });
|
|
56281
|
+
setTimeout(() => process.exit(0), 250);
|
|
56204
56282
|
});
|
|
56205
|
-
app.get("/api/
|
|
56206
|
-
|
|
56207
|
-
|
|
56208
|
-
|
|
56209
|
-
|
|
56210
|
-
const
|
|
56211
|
-
|
|
56212
|
-
|
|
56213
|
-
|
|
56283
|
+
app.get("/api/aware/update", async () => {
|
|
56284
|
+
return { ok: true, ...await checkAwareUpdate() };
|
|
56285
|
+
});
|
|
56286
|
+
let awareInstallInFlight = false;
|
|
56287
|
+
app.post("/api/aware/update/apply", async (_req, reply) => {
|
|
56288
|
+
const block = awareUpgradeBlockReason({
|
|
56289
|
+
bootstrapInstalling: getBootstrapState().status === "installing",
|
|
56290
|
+
installInFlight: awareInstallInFlight,
|
|
56291
|
+
runActive: isRunActive(),
|
|
56292
|
+
hostWatcherActive: hostWatcherActive()
|
|
56293
|
+
});
|
|
56294
|
+
if (block) return reply.status(409).send({ ok: false, error: block });
|
|
56295
|
+
awareInstallInFlight = true;
|
|
56296
|
+
try {
|
|
56297
|
+
const res = await applyAwareUpdate(awareApplyDeps(installAwareGlobal));
|
|
56298
|
+
if (!res.ok) return reply.status(409).send({ ok: false, error: res.error ?? "aware upgrade failed" });
|
|
56299
|
+
return { ok: true, version: res.version };
|
|
56300
|
+
} finally {
|
|
56301
|
+
awareInstallInFlight = false;
|
|
56302
|
+
}
|
|
56214
56303
|
});
|
|
56215
|
-
app.post(
|
|
56216
|
-
|
|
56217
|
-
|
|
56218
|
-
|
|
56219
|
-
const { appId, nodeId, instruction, snapshots } = req.body ?? {};
|
|
56220
|
-
if (!appId || !nodeId || !instruction) {
|
|
56221
|
-
return reply.status(400).send({ ok: false, error: "appId, nodeId, instruction required" });
|
|
56222
|
-
}
|
|
56223
|
-
let decoded;
|
|
56224
|
-
try {
|
|
56225
|
-
decoded = decodeSnapshots(snapshots);
|
|
56226
|
-
} catch (e) {
|
|
56227
|
-
return reply.status(400).send({ ok: false, error: e instanceof Error ? e.message : "bad snapshot" });
|
|
56228
|
-
}
|
|
56229
|
-
const request = addRequest({ type: "tweak", appId, nodeId, instruction }, decoded);
|
|
56230
|
-
broadcast({ type: "request-added", request });
|
|
56231
|
-
return { ok: true, request };
|
|
56304
|
+
app.post("/api/bootstrap/retry", async () => {
|
|
56305
|
+
const st = getBootstrapState().status;
|
|
56306
|
+
if (st === "failed" || st === "idle") {
|
|
56307
|
+
void runBootstrap(bootstrapDeps);
|
|
56232
56308
|
}
|
|
56233
|
-
|
|
56234
|
-
app.post("/api/use-template", async (req, reply) => {
|
|
56235
|
-
const { appId, templateId } = req.body ?? {};
|
|
56236
|
-
if (!appId || !templateId) return reply.status(400).send({ ok: false, error: "appId and templateId required" });
|
|
56237
|
-
const template = getTemplate(templateId);
|
|
56238
|
-
if (!template) return reply.status(404).send({ ok: false, error: "template not found" });
|
|
56239
|
-
const request = addRequest({ type: "use-template", appId, templateId, template });
|
|
56240
|
-
broadcast({ type: "request-added", request });
|
|
56241
|
-
return { ok: true, request };
|
|
56309
|
+
return { ok: true, bootstrap: getBootstrapState().status };
|
|
56242
56310
|
});
|
|
56243
|
-
|
|
56244
|
-
|
|
56245
|
-
|
|
56246
|
-
return { ok: true };
|
|
56311
|
+
onSeatLost((reason) => broadcast({ type: "seat-taken", reason }));
|
|
56312
|
+
app.get("/api/apps", async () => {
|
|
56313
|
+
const apps = await aware.list();
|
|
56314
|
+
return { ok: true, apps: apps.map((a) => ({ ...a, provider: appProvider(a.id) })) };
|
|
56247
56315
|
});
|
|
56248
|
-
app.
|
|
56249
|
-
const
|
|
56250
|
-
|
|
56251
|
-
return { ok: true, cleared };
|
|
56316
|
+
app.get("/api/agents", async () => {
|
|
56317
|
+
const agents = await aware.agentList();
|
|
56318
|
+
return { ok: true, agents };
|
|
56252
56319
|
});
|
|
56253
|
-
app.get("/api/
|
|
56254
|
-
|
|
56255
|
-
|
|
56256
|
-
"Content-Type": "text/event-stream",
|
|
56257
|
-
"Cache-Control": "no-cache",
|
|
56258
|
-
Connection: "keep-alive"
|
|
56259
|
-
});
|
|
56260
|
-
reply.raw.write(`data: ${JSON.stringify({ type: "connected" })}
|
|
56261
|
-
|
|
56262
|
-
`);
|
|
56263
|
-
addClient(reply);
|
|
56320
|
+
app.get("/api/agent/:id", async (req) => {
|
|
56321
|
+
const agent = await aware.agentDescribe(req.params.id);
|
|
56322
|
+
return { ok: true, agent };
|
|
56264
56323
|
});
|
|
56265
|
-
|
|
56266
|
-
|
|
56267
|
-
|
|
56268
|
-
|
|
56269
|
-
|
|
56270
|
-
|
|
56271
|
-
|
|
56272
|
-
|
|
56273
|
-
|
|
56274
|
-
|
|
56275
|
-
|
|
56276
|
-
|
|
56277
|
-
|
|
56278
|
-
process.on("SIGINT", close);
|
|
56279
|
-
process.on("SIGTERM", close);
|
|
56280
|
-
await app.listen({ port: PORT, host: HOST });
|
|
56281
|
-
app.log.info(`floless.app on http://${HOST}:${PORT} (aware npm ${getBootstrapState().awareVersion ?? "unresolved"})`);
|
|
56282
|
-
try {
|
|
56283
|
-
const synced = syncSkills().filter((r) => r.action === "installed" || r.action === "updated");
|
|
56284
|
-
if (synced.length) {
|
|
56285
|
-
app.log.info(
|
|
56286
|
-
{ skills: synced.map((s) => `${s.runtime}:${s.skill}@${s.to}`) },
|
|
56287
|
-
`synced ${synced.length} floless.app skill(s) into AI terminal(s)`
|
|
56288
|
-
);
|
|
56324
|
+
app.get("/api/integrations", async () => {
|
|
56325
|
+
const integrations = listIntegrations();
|
|
56326
|
+
try {
|
|
56327
|
+
const live = await aware.connectList();
|
|
56328
|
+
const byId = new Map(live.map((c) => [c.integration, c]));
|
|
56329
|
+
for (const it of integrations) {
|
|
56330
|
+
const c = byId.get(it.id);
|
|
56331
|
+
if (!c) continue;
|
|
56332
|
+
it.status = c.status === "valid" ? "connected" : c.status === "expired" ? "expired" : "disconnected";
|
|
56333
|
+
it.expiresAt = c.expires_in_secs != null ? new Date(Date.now() + c.expires_in_secs * 1e3).toISOString() : it.expiresAt;
|
|
56334
|
+
}
|
|
56335
|
+
} catch (err) {
|
|
56336
|
+
app.log.warn({ err: String(err) }, "aware connect --list unavailable; using file-based status");
|
|
56289
56337
|
}
|
|
56290
|
-
|
|
56291
|
-
app.log.warn({ err: String(err) }, "skill sync failed (non-fatal)");
|
|
56292
|
-
}
|
|
56293
|
-
}
|
|
56294
|
-
|
|
56295
|
-
// launch.mjs
|
|
56296
|
-
var import_node_child_process6 = require("node:child_process");
|
|
56297
|
-
var import_node_path17 = require("node:path");
|
|
56298
|
-
var import_node_url3 = require("node:url");
|
|
56299
|
-
var import_node_fs18 = require("node:fs");
|
|
56300
|
-
var import_node_http = __toESM(require("node:http"), 1);
|
|
56301
|
-
var import_node_readline = require("node:readline");
|
|
56302
|
-
var __dirname4 = (0, import_node_path17.dirname)((0, import_node_url3.fileURLToPath)(__import_meta_url));
|
|
56303
|
-
var PORT2 = Number(process.env.PORT ?? 4317);
|
|
56304
|
-
var HEALTH_URL = `http://127.0.0.1:${PORT2}/api/health`;
|
|
56305
|
-
var BROWSER_URL = `http://floless.localhost:${PORT2}`;
|
|
56306
|
-
var isWin2 = process.platform === "win32";
|
|
56307
|
-
var log = (m) => {
|
|
56308
|
-
try {
|
|
56309
|
-
process.stdout.write(`floless: ${m}
|
|
56310
|
-
`);
|
|
56311
|
-
} catch {
|
|
56312
|
-
}
|
|
56313
|
-
};
|
|
56314
|
-
function ping() {
|
|
56315
|
-
return new Promise((resolve4) => {
|
|
56316
|
-
const req = import_node_http.default.get(HEALTH_URL, { timeout: 1500 }, (res) => {
|
|
56317
|
-
res.resume();
|
|
56318
|
-
resolve4(res.statusCode === 200);
|
|
56319
|
-
});
|
|
56320
|
-
req.on("error", () => resolve4(false));
|
|
56321
|
-
req.on("timeout", () => {
|
|
56322
|
-
req.destroy();
|
|
56323
|
-
resolve4(false);
|
|
56324
|
-
});
|
|
56338
|
+
return { ok: true, integrations };
|
|
56325
56339
|
});
|
|
56326
|
-
}
|
|
56327
|
-
|
|
56328
|
-
|
|
56329
|
-
const
|
|
56330
|
-
|
|
56331
|
-
|
|
56332
|
-
|
|
56333
|
-
|
|
56334
|
-
|
|
56335
|
-
try {
|
|
56336
|
-
resolve4(JSON.parse(body).appVersion || null);
|
|
56337
|
-
} catch {
|
|
56338
|
-
resolve4(null);
|
|
56340
|
+
const DEFAULT_CONNECT_FLOW = {};
|
|
56341
|
+
app.post("/api/connect/:id", async (req) => {
|
|
56342
|
+
const id = req.params.id;
|
|
56343
|
+
const requested = req.body?.flow;
|
|
56344
|
+
const flow = requested === "device-code" || requested === "oauth" ? requested : DEFAULT_CONNECT_FLOW[id] ?? "oauth";
|
|
56345
|
+
if (flow === "device-code") {
|
|
56346
|
+
const { prompt } = await aware.connectDeviceCode(id, (event) => {
|
|
56347
|
+
if (event.phase === "result") {
|
|
56348
|
+
broadcast({ type: "connect-result", id, status: event.status ?? "unknown", error: event.error ?? null });
|
|
56339
56349
|
}
|
|
56340
56350
|
});
|
|
56351
|
+
return { ok: true, id, flow, prompt };
|
|
56352
|
+
}
|
|
56353
|
+
aware.connectOAuth(id, (result) => {
|
|
56354
|
+
broadcast({
|
|
56355
|
+
type: "connect-result",
|
|
56356
|
+
id,
|
|
56357
|
+
status: result.status === "connected" ? "connected" : "failed",
|
|
56358
|
+
error: result.status === "connected" ? null : "sign-in failed or was cancelled"
|
|
56359
|
+
});
|
|
56341
56360
|
});
|
|
56342
|
-
|
|
56343
|
-
req.on("timeout", () => {
|
|
56344
|
-
req.destroy();
|
|
56345
|
-
resolve4(null);
|
|
56346
|
-
});
|
|
56361
|
+
return { ok: true, id, flow, started: true };
|
|
56347
56362
|
});
|
|
56348
|
-
|
|
56349
|
-
|
|
56350
|
-
|
|
56351
|
-
|
|
56352
|
-
|
|
56353
|
-
const
|
|
56354
|
-
if (
|
|
56355
|
-
|
|
56356
|
-
|
|
56357
|
-
}
|
|
56358
|
-
|
|
56359
|
-
|
|
56360
|
-
|
|
56361
|
-
}
|
|
56362
|
-
|
|
56363
|
-
|
|
56364
|
-
|
|
56365
|
-
|
|
56366
|
-
|
|
56367
|
-
|
|
56368
|
-
|
|
56369
|
-
|
|
56370
|
-
|
|
56371
|
-
|
|
56372
|
-
|
|
56373
|
-
|
|
56374
|
-
|
|
56375
|
-
|
|
56376
|
-
|
|
56377
|
-
|
|
56378
|
-
|
|
56379
|
-
|
|
56380
|
-
|
|
56381
|
-
|
|
56382
|
-
|
|
56383
|
-
|
|
56384
|
-
|
|
56385
|
-
|
|
56386
|
-
|
|
56387
|
-
|
|
56363
|
+
app.get("/api/app/:id", async (req) => {
|
|
56364
|
+
return { ok: true, app: readApp(req.params.id) };
|
|
56365
|
+
});
|
|
56366
|
+
app.post("/api/compile", async (req, reply) => {
|
|
56367
|
+
const { id, sourcePath } = req.body ?? {};
|
|
56368
|
+
const path = sourcePath ?? (id ? readApp(id).source.path : void 0);
|
|
56369
|
+
if (!path) return reply.status(400).send({ ok: false, error: "id or sourcePath required" });
|
|
56370
|
+
const result = await aware.compile(path);
|
|
56371
|
+
broadcast({ type: "compiled", id: id ?? null, lockPath: result.lockPath });
|
|
56372
|
+
return { ok: true, result };
|
|
56373
|
+
});
|
|
56374
|
+
app.post("/api/bake", async (req, reply) => {
|
|
56375
|
+
const { id } = req.body ?? {};
|
|
56376
|
+
if (!id) return reply.status(400).send({ ok: false, error: "id required" });
|
|
56377
|
+
const appData = readApp(id);
|
|
56378
|
+
if (!appData.runnable) {
|
|
56379
|
+
return reply.status(409).send({ ok: false, error: "app not runnable \u2014 compile the lock first, then bake" });
|
|
56380
|
+
}
|
|
56381
|
+
const inputs = appData.inputs.map((i) => ({ name: i.name, type: i.type }));
|
|
56382
|
+
const baked = bakeFloSource(appData.source.text, inputs);
|
|
56383
|
+
const tmpRoot = (0, import_node_fs18.mkdtempSync)((0, import_node_path17.join)((0, import_node_os11.tmpdir)(), "floless-bake-"));
|
|
56384
|
+
const backupDir = (0, import_node_path17.join)(tmpRoot, `${id}-backup`);
|
|
56385
|
+
const bakeDir = (0, import_node_path17.join)(tmpRoot, id);
|
|
56386
|
+
(0, import_node_fs18.cpSync)(appDir(id), backupDir, { recursive: true });
|
|
56387
|
+
(0, import_node_fs18.cpSync)(appDir(id), bakeDir, { recursive: true });
|
|
56388
|
+
const floName = appData.source.path.split(/[\\/]/).pop();
|
|
56389
|
+
(0, import_node_fs18.writeFileSync)((0, import_node_path17.join)(bakeDir, floName), baked);
|
|
56390
|
+
let appInstalled = true;
|
|
56391
|
+
try {
|
|
56392
|
+
await aware.uninstall(id);
|
|
56393
|
+
appInstalled = false;
|
|
56394
|
+
try {
|
|
56395
|
+
await aware.install(bakeDir);
|
|
56396
|
+
appInstalled = true;
|
|
56397
|
+
} catch (installErr) {
|
|
56398
|
+
try {
|
|
56399
|
+
await aware.install(backupDir);
|
|
56400
|
+
appInstalled = true;
|
|
56401
|
+
} catch (restoreErr) {
|
|
56402
|
+
app.log.error({ id, tmpRoot, restoreErr: String(restoreErr) }, "bake: install AND restore failed \u2014 app uninstalled; backup retained");
|
|
56403
|
+
return reply.status(502).send({
|
|
56404
|
+
ok: false,
|
|
56405
|
+
error: `bake failed and the app could not be restored \u2014 its source is backed up at ${backupDir}. Reinstall it from there with: aware app install "${backupDir}"`
|
|
56406
|
+
});
|
|
56407
|
+
}
|
|
56408
|
+
throw installErr;
|
|
56409
|
+
}
|
|
56410
|
+
try {
|
|
56411
|
+
await aware.compile((0, import_node_path17.join)(appDir(id), floName));
|
|
56412
|
+
} catch (compileErr) {
|
|
56413
|
+
app.log.warn({ id, compileErr: String(compileErr) }, "bake: post-install recompile failed (app baked but may need a manual Compile)");
|
|
56414
|
+
}
|
|
56415
|
+
broadcast({ type: "baked", id });
|
|
56416
|
+
return { ok: true, id, agent: id, inputs };
|
|
56417
|
+
} finally {
|
|
56418
|
+
if (appInstalled) (0, import_node_fs18.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
56419
|
+
}
|
|
56420
|
+
});
|
|
56421
|
+
const graftAgentsDir = () => (0, import_node_path17.join)((0, import_node_os11.homedir)(), ".aware", "agents");
|
|
56422
|
+
app.post("/api/graft/match", async (req, reply) => {
|
|
56423
|
+
const { glob } = req.body ?? {};
|
|
56424
|
+
if (!glob) return reply.status(400).send({ ok: false, error: "glob required" });
|
|
56425
|
+
let safe;
|
|
56426
|
+
try {
|
|
56427
|
+
safe = assertSourceRef("dlls", glob);
|
|
56428
|
+
} catch (e) {
|
|
56429
|
+
return reply.status(400).send({ ok: false, error: e.message });
|
|
56430
|
+
}
|
|
56431
|
+
return { ok: true, files: matchAssemblies(safe) };
|
|
56388
56432
|
});
|
|
56389
|
-
|
|
56390
|
-
}
|
|
56391
|
-
|
|
56392
|
-
|
|
56393
|
-
|
|
56394
|
-
|
|
56395
|
-
|
|
56396
|
-
if (!isWin2) {
|
|
56433
|
+
app.post("/api/graft/introspect", async (req, reply) => {
|
|
56434
|
+
const { sourceKind, sourceRef, decompile, outputId, acceptLicense, referenceDirs } = req.body ?? {};
|
|
56435
|
+
if (!sourceKind || !sourceRef) {
|
|
56436
|
+
return reply.status(400).send({ ok: false, error: "sourceKind and sourceRef required" });
|
|
56437
|
+
}
|
|
56438
|
+
const tempHome = (0, import_node_fs18.mkdtempSync)((0, import_node_path17.join)((0, import_node_os11.tmpdir)(), "floless-graft-"));
|
|
56439
|
+
let result;
|
|
56397
56440
|
try {
|
|
56398
|
-
|
|
56399
|
-
|
|
56400
|
-
|
|
56401
|
-
|
|
56441
|
+
result = await aware.build({
|
|
56442
|
+
sourceKind,
|
|
56443
|
+
sourceRef,
|
|
56444
|
+
decompile,
|
|
56445
|
+
outputId,
|
|
56446
|
+
acceptLicense,
|
|
56447
|
+
referenceDirs,
|
|
56448
|
+
awareHome: tempHome
|
|
56449
|
+
});
|
|
56450
|
+
} catch (err) {
|
|
56451
|
+
(0, import_node_fs18.rmSync)(tempHome, { recursive: true, force: true });
|
|
56452
|
+
const msg = err instanceof AwareError ? err.message : String(err?.message ?? err);
|
|
56453
|
+
return reply.status(422).send({ ok: false, error: msg });
|
|
56402
56454
|
}
|
|
56403
|
-
|
|
56404
|
-
|
|
56405
|
-
|
|
56406
|
-
|
|
56407
|
-
return true;
|
|
56408
|
-
} catch {
|
|
56409
|
-
return false;
|
|
56410
|
-
}
|
|
56411
|
-
}
|
|
56412
|
-
async function ensureServerUp() {
|
|
56413
|
-
if (await ping()) return;
|
|
56414
|
-
log("starting server\u2026");
|
|
56415
|
-
startServerDetached();
|
|
56416
|
-
if (!await waitHealthy()) {
|
|
56417
|
-
log(`server did not become healthy on ${HEALTH_URL} within 30s \u2014 check for errors with "npm run dev"`);
|
|
56418
|
-
process.exit(1);
|
|
56419
|
-
}
|
|
56420
|
-
log("server up");
|
|
56421
|
-
}
|
|
56422
|
-
async function cmdOpen() {
|
|
56423
|
-
if (await ping()) {
|
|
56424
|
-
const running = await probeVersion();
|
|
56425
|
-
if (shouldTakeOver(running, _selfVersion)) {
|
|
56426
|
-
log(`floless.app v${running} is already running on ${PORT2}; this build is v${_selfVersion} \u2014 taking over`);
|
|
56427
|
-
stopServer();
|
|
56428
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
56429
|
-
await ensureServerUp();
|
|
56430
|
-
} else {
|
|
56431
|
-
log(`already running${running ? ` (v${running})` : ""} \u2014 opening browser`);
|
|
56455
|
+
const manifest = readStagedManifest(result.agentDir);
|
|
56456
|
+
if (!manifest) {
|
|
56457
|
+
(0, import_node_fs18.rmSync)(tempHome, { recursive: true, force: true });
|
|
56458
|
+
return reply.status(502).send({ ok: false, error: `build produced output at ${result.agentDir} but no manifest.yaml` });
|
|
56432
56459
|
}
|
|
56433
|
-
|
|
56434
|
-
|
|
56435
|
-
|
|
56436
|
-
|
|
56437
|
-
|
|
56438
|
-
}
|
|
56439
|
-
async function cmdStart() {
|
|
56440
|
-
if (await ping()) {
|
|
56441
|
-
log("already running");
|
|
56442
|
-
return;
|
|
56443
|
-
}
|
|
56444
|
-
await ensureServerUp();
|
|
56445
|
-
}
|
|
56446
|
-
var SUPERVISE_POLL_MS = 3e3;
|
|
56447
|
-
var SUPERVISE_RESPAWN_ENV = "FLOLESS_SUPERVISE_RESPAWNED";
|
|
56448
|
-
async function cmdSupervise() {
|
|
56449
|
-
const isSeaChannel = /flolessapp\.exe$/i.test(process.execPath);
|
|
56450
|
-
if (isSeaChannel && !process.env[SUPERVISE_RESPAWN_ENV]) {
|
|
56451
|
-
rotateLog();
|
|
56452
|
-
const fd = openLogFd();
|
|
56453
|
-
const child = (0, import_node_child_process6.spawn)(process.execPath, ["--supervise"], {
|
|
56454
|
-
detached: true,
|
|
56455
|
-
stdio: fd == null ? "ignore" : ["ignore", fd, fd],
|
|
56456
|
-
windowsHide: true,
|
|
56457
|
-
env: { ...process.env, [SUPERVISE_RESPAWN_ENV]: "1" }
|
|
56458
|
-
});
|
|
56459
|
-
child.unref();
|
|
56460
|
-
log("supervisor re-spawned detached");
|
|
56461
|
-
return;
|
|
56462
|
-
}
|
|
56463
|
-
log("supervisor up \u2014 keeping the server alive");
|
|
56464
|
-
for (; ; ) {
|
|
56465
|
-
if (!await ping()) {
|
|
56466
|
-
log("server not responding \u2014 (re)starting");
|
|
56467
|
-
startServerDetached();
|
|
56468
|
-
log(await waitHealthy() ? "server up" : "server not healthy yet \u2014 will retry");
|
|
56460
|
+
const token = (0, import_node_crypto5.randomUUID)();
|
|
56461
|
+
registerStage(token, tempHome, result.agentId);
|
|
56462
|
+
const preview = buildPreview(manifest, sourceKind, sourceRef, token);
|
|
56463
|
+
if ((0, import_node_fs18.existsSync)((0, import_node_path17.join)(graftAgentsDir(), result.agentId))) {
|
|
56464
|
+
preview.warnings.unshift(`An agent named "${result.agentId}" is already installed \u2014 creating it will overwrite it.`);
|
|
56469
56465
|
}
|
|
56470
|
-
|
|
56471
|
-
}
|
|
56472
|
-
|
|
56473
|
-
|
|
56474
|
-
|
|
56475
|
-
|
|
56476
|
-
|
|
56477
|
-
|
|
56478
|
-
log(`stopping server on port ${PORT2}\u2026`);
|
|
56479
|
-
for (let i = 0; i < 10; i++) {
|
|
56480
|
-
if (!await ping()) {
|
|
56481
|
-
log("stopped");
|
|
56482
|
-
return;
|
|
56466
|
+
return { ok: true, preview };
|
|
56467
|
+
});
|
|
56468
|
+
app.post("/api/graft/commit", async (req, reply) => {
|
|
56469
|
+
const { stagedRef, force = false } = req.body ?? {};
|
|
56470
|
+
if (!stagedRef) return reply.status(400).send({ ok: false, error: "stagedRef required" });
|
|
56471
|
+
const stage = consumeStage(stagedRef);
|
|
56472
|
+
if (!stage) {
|
|
56473
|
+
return reply.status(410).send({ ok: false, error: "staged build expired or not found \u2014 introspect again" });
|
|
56483
56474
|
}
|
|
56484
|
-
await new Promise((r) => setTimeout(r, 300));
|
|
56485
|
-
}
|
|
56486
|
-
log("warning: a process may still be holding the port");
|
|
56487
|
-
}
|
|
56488
|
-
function parseTeardownFlags(argv = []) {
|
|
56489
|
-
return {
|
|
56490
|
-
purge: argv.includes("--purge"),
|
|
56491
|
-
keepAware: argv.includes("--keep-aware")
|
|
56492
|
-
};
|
|
56493
|
-
}
|
|
56494
|
-
function parseProcessList(json) {
|
|
56495
|
-
let parsed;
|
|
56496
|
-
try {
|
|
56497
|
-
parsed = JSON.parse(json || "[]");
|
|
56498
|
-
} catch {
|
|
56499
|
-
return [];
|
|
56500
|
-
}
|
|
56501
|
-
const arr = Array.isArray(parsed) ? parsed : [parsed];
|
|
56502
|
-
return arr.filter((p) => p && p.ProcessId != null).map((p) => ({ pid: Number(p.ProcessId), cmd: String(p.CommandLine ?? "") }));
|
|
56503
|
-
}
|
|
56504
|
-
function enumerateProcesses() {
|
|
56505
|
-
if (!isWin2) return [];
|
|
56506
|
-
try {
|
|
56507
|
-
const ps = "Get-CimInstance Win32_Process | Select-Object ProcessId,CommandLine | ConvertTo-Json -Compress";
|
|
56508
|
-
const out = (0, import_node_child_process6.execSync)(`powershell -NoProfile -Command "${ps}"`, {
|
|
56509
|
-
encoding: "utf8",
|
|
56510
|
-
windowsHide: true,
|
|
56511
|
-
maxBuffer: 16 * 1024 * 1024
|
|
56512
|
-
});
|
|
56513
|
-
return parseProcessList(out);
|
|
56514
|
-
} catch {
|
|
56515
|
-
return [];
|
|
56516
|
-
}
|
|
56517
|
-
}
|
|
56518
|
-
function killSupervisor() {
|
|
56519
|
-
if (!isWin2) return;
|
|
56520
|
-
const isNpmChannel = /^node(\.exe)?$/i.test((0, import_node_path17.basename)(process.execPath));
|
|
56521
|
-
const scriptMatch = isNpmChannel ? (0, import_node_path17.basename)((0, import_node_url3.fileURLToPath)(__import_meta_url)) : void 0;
|
|
56522
|
-
const realExe = resolveRealInstallExe(process.execPath);
|
|
56523
|
-
const exeMatch = realExe === process.execPath ? process.execPath : [process.execPath, realExe];
|
|
56524
|
-
const pids = supervisorPidsToKill(enumerateProcesses(), process.pid, exeMatch, scriptMatch);
|
|
56525
|
-
for (const pid of pids) {
|
|
56526
|
-
log(`stopping supervisor (pid ${pid})\u2026`);
|
|
56527
56475
|
try {
|
|
56528
|
-
(
|
|
56529
|
-
} catch {
|
|
56530
|
-
|
|
56531
|
-
|
|
56532
|
-
}
|
|
56533
|
-
|
|
56534
|
-
|
|
56535
|
-
|
|
56536
|
-
const deadline = Date.now() + 3e3;
|
|
56537
|
-
while (Date.now() < deadline) {
|
|
56538
|
-
await new Promise((r) => setTimeout(r, 300));
|
|
56539
|
-
if (await ping()) {
|
|
56540
|
-
log("server respawned after kill \u2014 re-killing supervisor and stopping again");
|
|
56541
|
-
killSupervisor();
|
|
56542
|
-
stopServer();
|
|
56476
|
+
commitStaged(stage.tempDir, stage.agentId, graftAgentsDir(), force);
|
|
56477
|
+
} catch (err) {
|
|
56478
|
+
if (err instanceof GraftCommitError && err.code === "collision") {
|
|
56479
|
+
registerStage(stagedRef, stage.tempDir, stage.agentId);
|
|
56480
|
+
return reply.status(409).send({ ok: false, error: err.message, agentId: stage.agentId, collision: true });
|
|
56481
|
+
}
|
|
56482
|
+
(0, import_node_fs18.rmSync)(stage.tempDir, { recursive: true, force: true });
|
|
56483
|
+
throw err;
|
|
56543
56484
|
}
|
|
56544
|
-
|
|
56545
|
-
|
|
56546
|
-
|
|
56547
|
-
|
|
56548
|
-
|
|
56549
|
-
|
|
56550
|
-
|
|
56551
|
-
|
|
56552
|
-
|
|
56553
|
-
|
|
56554
|
-
|
|
56555
|
-
|
|
56556
|
-
|
|
56557
|
-
|
|
56558
|
-
|
|
56559
|
-
|
|
56560
|
-
|
|
56561
|
-
|
|
56562
|
-
|
|
56485
|
+
broadcast({ type: "grafted", id: stage.agentId });
|
|
56486
|
+
return { ok: true, agentId: stage.agentId };
|
|
56487
|
+
});
|
|
56488
|
+
app.post(
|
|
56489
|
+
"/api/run",
|
|
56490
|
+
async (req, reply) => {
|
|
56491
|
+
const { id, dryRun, simulate, inputs } = req.body ?? {};
|
|
56492
|
+
if (!id) return reply.status(400).send({ ok: false, error: "id required" });
|
|
56493
|
+
const blocked = hostRunBlocked(appProvider(id));
|
|
56494
|
+
if (blocked) return reply.send({ ok: false, error: blocked, blocked: true, simulate: !!simulate });
|
|
56495
|
+
let result;
|
|
56496
|
+
try {
|
|
56497
|
+
result = await aware.run(id, { dryRun, simulate, inputs });
|
|
56498
|
+
} catch (err) {
|
|
56499
|
+
if (err instanceof AwareError) {
|
|
56500
|
+
const detail = err.detail && typeof err.detail === "object" ? err.detail : null;
|
|
56501
|
+
if (detail && detail.cancelled === true) {
|
|
56502
|
+
app.log.info({ id }, "run cancelled by user");
|
|
56503
|
+
return reply.send({ ok: false, error: "Run cancelled", cancelled: true, simulate: !!simulate });
|
|
56504
|
+
}
|
|
56505
|
+
const stderr = detail && typeof detail.stderr === "string" ? detail.stderr.trim() : "";
|
|
56506
|
+
app.log.warn({ detail: err.detail }, err.message);
|
|
56507
|
+
return reply.send({ ok: false, error: err.message, reason: stderr || null, simulate: !!simulate });
|
|
56563
56508
|
}
|
|
56564
|
-
|
|
56565
|
-
|
|
56566
|
-
|
|
56567
|
-
|
|
56568
|
-
|
|
56569
|
-
|
|
56570
|
-
|
|
56571
|
-
|
|
56509
|
+
throw err;
|
|
56510
|
+
}
|
|
56511
|
+
const events = result.traceText ? parseTrace(result.traceText) : [];
|
|
56512
|
+
broadcast({ type: "run-started", id, dryRun: !!dryRun, simulate: !!simulate, runId: result.runId });
|
|
56513
|
+
for (const ev of events) broadcast({ type: "trace", id, event: ev });
|
|
56514
|
+
broadcast({ type: "run-ended", id, runId: result.runId });
|
|
56515
|
+
const report = extractReportHtml(events);
|
|
56516
|
+
return { ok: true, runId: result.runId, tracePath: result.tracePath, events, report };
|
|
56517
|
+
}
|
|
56518
|
+
);
|
|
56519
|
+
app.post("/api/run/stop", async () => {
|
|
56520
|
+
const running = cancelActiveRun();
|
|
56521
|
+
return { ok: true, running };
|
|
56572
56522
|
});
|
|
56573
|
-
|
|
56574
|
-
|
|
56575
|
-
|
|
56576
|
-
|
|
56577
|
-
|
|
56578
|
-
|
|
56579
|
-
|
|
56580
|
-
|
|
56523
|
+
function resolveArgs(configArgs, inputs) {
|
|
56524
|
+
const out = {};
|
|
56525
|
+
for (const [k, v] of Object.entries(configArgs)) {
|
|
56526
|
+
if (typeof v === "string") {
|
|
56527
|
+
const m = v.match(/^\{\{\s*inputs\.([A-Za-z0-9._-]+)\s*\}\}$/);
|
|
56528
|
+
out[k] = m && m[1] != null && m[1] in inputs ? inputs[m[1]] : v;
|
|
56529
|
+
} else {
|
|
56530
|
+
out[k] = v;
|
|
56531
|
+
}
|
|
56581
56532
|
}
|
|
56533
|
+
return out;
|
|
56582
56534
|
}
|
|
56583
|
-
|
|
56584
|
-
|
|
56585
|
-
|
|
56586
|
-
|
|
56587
|
-
|
|
56588
|
-
|
|
56589
|
-
|
|
56590
|
-
|
|
56591
|
-
return;
|
|
56535
|
+
app.post(
|
|
56536
|
+
"/api/debug-node",
|
|
56537
|
+
async (req, reply) => {
|
|
56538
|
+
const { id, nodeId, inputs } = req.body ?? {};
|
|
56539
|
+
if (!id || !nodeId) return reply.status(400).send({ ok: false, error: "id and nodeId required" });
|
|
56540
|
+
const appData = readApp(id);
|
|
56541
|
+
if (!appData.runnable) return reply.status(409).send({ ok: false, error: "app not runnable \u2014 compile the lock first" });
|
|
56542
|
+
const node = appData.nodes.find((n) => n.id === nodeId);
|
|
56543
|
+
if (!node) return reply.status(404).send({ ok: false, error: `node not found: ${nodeId}` });
|
|
56544
|
+
const code = node.config?.code;
|
|
56545
|
+
if (typeof code !== "string") return reply.status(400).send({ ok: false, error: `node ${nodeId} has no exec code` });
|
|
56546
|
+
const version = typeof node.config?.version === "string" ? node.config.version : void 0;
|
|
56547
|
+
const configArgs = node.config?.args ?? {};
|
|
56548
|
+
const effectiveInputs = {};
|
|
56549
|
+
for (const inp of appData.inputs) {
|
|
56550
|
+
const d = inp.default;
|
|
56551
|
+
if (typeof d === "string" || typeof d === "number" || typeof d === "boolean") effectiveInputs[inp.name] = d;
|
|
56552
|
+
}
|
|
56553
|
+
Object.assign(effectiveInputs, inputs ?? {});
|
|
56554
|
+
const args = resolveArgs(configArgs, effectiveInputs);
|
|
56555
|
+
broadcast({ type: "debug-started", id, nodeId });
|
|
56556
|
+
const result = await aware.execTekla(code, { version, args, debug: true });
|
|
56557
|
+
broadcast({ type: "debug-ended", id, nodeId });
|
|
56558
|
+
const r = result.result ?? {};
|
|
56559
|
+
const html = typeof r.html === "string" ? r.html : null;
|
|
56560
|
+
return { ok: true, result, report: html ? { nodeId, html } : null };
|
|
56592
56561
|
}
|
|
56593
|
-
|
|
56594
|
-
|
|
56595
|
-
|
|
56562
|
+
);
|
|
56563
|
+
app.get("/api/routines", async () => ({
|
|
56564
|
+
// Annotate each with live state so the UI reconciles from the server (a dropped
|
|
56565
|
+
// SSE event can't wedge an indicator): schedule routines carry queued/active
|
|
56566
|
+
// `running`; trigger routines carry their live `session` snapshot
|
|
56567
|
+
// (listening/fired×N/error/stopped).
|
|
56568
|
+
ok: true,
|
|
56569
|
+
routines: listRoutines().map(
|
|
56570
|
+
(r) => r.kind === "trigger" ? { ...r, session: getSnapshot(r.id) } : { ...r, running: isRoutineRunning(r.id) }
|
|
56571
|
+
),
|
|
56572
|
+
max: MAX_ROUTINES
|
|
56573
|
+
}));
|
|
56574
|
+
app.post(
|
|
56575
|
+
"/api/routines",
|
|
56576
|
+
async (req, reply) => {
|
|
56577
|
+
const routine = createRoutine(req.body ?? {});
|
|
56578
|
+
return reply.status(201).send({ ok: true, routine });
|
|
56596
56579
|
}
|
|
56597
|
-
|
|
56598
|
-
|
|
56599
|
-
|
|
56600
|
-
|
|
56601
|
-
|
|
56602
|
-
|
|
56603
|
-
|
|
56604
|
-
try {
|
|
56605
|
-
cmd = (new URL(arg).hostname || "open").toLowerCase();
|
|
56606
|
-
} catch {
|
|
56607
|
-
cmd = "open";
|
|
56580
|
+
);
|
|
56581
|
+
app.patch(
|
|
56582
|
+
"/api/routines/:id",
|
|
56583
|
+
async (req, reply) => {
|
|
56584
|
+
const routine = updateRoutine(req.params.id, req.body ?? {});
|
|
56585
|
+
if (!routine) return reply.status(404).send({ ok: false, error: "routine not found" });
|
|
56586
|
+
return { ok: true, routine };
|
|
56608
56587
|
}
|
|
56609
|
-
|
|
56610
|
-
|
|
56611
|
-
}
|
|
56612
|
-
|
|
56613
|
-
log('this is the npm build \u2014 update it with "npm i -g @floless/app"');
|
|
56614
|
-
log('(the installer build self-updates via "floless-app update")');
|
|
56615
|
-
}
|
|
56616
|
-
function removeRegistryFootprint() {
|
|
56617
|
-
if (!isWin2) return;
|
|
56618
|
-
try {
|
|
56619
|
-
(0, import_node_child_process6.execFileSync)("reg", ["delete", RUN_KEY, "/v", RUN_VALUE, "/f"], { stdio: "ignore", windowsHide: true });
|
|
56620
|
-
} catch {
|
|
56621
|
-
}
|
|
56622
|
-
try {
|
|
56623
|
-
(0, import_node_child_process6.execFileSync)("reg", ["delete", PROTOCOL_KEY, "/f"], { stdio: "ignore", windowsHide: true });
|
|
56624
|
-
} catch {
|
|
56625
|
-
}
|
|
56626
|
-
}
|
|
56627
|
-
function promptYesNo(question) {
|
|
56628
|
-
return new Promise((resolve4) => {
|
|
56629
|
-
const rl = (0, import_node_readline.createInterface)({ input: process.stdin, output: process.stdout });
|
|
56630
|
-
rl.question(`${question} `, (answer) => {
|
|
56631
|
-
rl.close();
|
|
56632
|
-
resolve4(/^y(es)?$/i.test((answer ?? "").trim()));
|
|
56633
|
-
});
|
|
56588
|
+
);
|
|
56589
|
+
app.delete("/api/routines/:id", async (req, reply) => {
|
|
56590
|
+
if (!deleteRoutine(req.params.id)) return reply.status(404).send({ ok: false, error: "routine not found" });
|
|
56591
|
+
return { ok: true };
|
|
56634
56592
|
});
|
|
56635
|
-
|
|
56636
|
-
|
|
56637
|
-
|
|
56638
|
-
|
|
56639
|
-
|
|
56640
|
-
|
|
56641
|
-
|
|
56642
|
-
|
|
56643
|
-
|
|
56644
|
-
|
|
56645
|
-
|
|
56646
|
-
|
|
56647
|
-
|
|
56648
|
-
try {
|
|
56649
|
-
(0, import_node_child_process6.execSync)("npm uninstall -g @aware-aeco/cli", { stdio: "inherit", shell: isWin2 });
|
|
56650
|
-
} catch {
|
|
56651
|
-
log('warning: "npm uninstall -g @aware-aeco/cli" failed \u2014 see the output above.');
|
|
56593
|
+
app.post("/api/routines/:id/run", async (req, reply) => {
|
|
56594
|
+
if (!runNow(req.params.id)) return reply.status(404).send({ ok: false, error: "routine not found" });
|
|
56595
|
+
return { ok: true };
|
|
56596
|
+
});
|
|
56597
|
+
app.get("/api/templates", async () => ({ ok: true, templates: listTemplates() }));
|
|
56598
|
+
app.post(
|
|
56599
|
+
"/api/templates",
|
|
56600
|
+
async (req, reply) => {
|
|
56601
|
+
const { name, category, node, source } = req.body ?? {};
|
|
56602
|
+
if (!name || !node) return reply.status(400).send({ ok: false, error: "name and node required" });
|
|
56603
|
+
const tpl = addTemplate({ name, category, node, source });
|
|
56604
|
+
broadcast({ type: "templates-changed" });
|
|
56605
|
+
return { ok: true, template: tpl };
|
|
56652
56606
|
}
|
|
56653
|
-
|
|
56654
|
-
|
|
56655
|
-
|
|
56656
|
-
|
|
56657
|
-
|
|
56658
|
-
|
|
56659
|
-
|
|
56660
|
-
|
|
56661
|
-
|
|
56607
|
+
);
|
|
56608
|
+
app.delete("/api/templates/:id", async (req, reply) => {
|
|
56609
|
+
if (!deleteTemplate(req.params.id)) return reply.status(404).send({ ok: false, error: "template not found" });
|
|
56610
|
+
broadcast({ type: "templates-changed" });
|
|
56611
|
+
return { ok: true };
|
|
56612
|
+
});
|
|
56613
|
+
app.get("/api/requests", async () => ({ ok: true, requests: listRequests() }));
|
|
56614
|
+
app.get("/api/requests/:id/snapshot/:n", async (req, reply) => {
|
|
56615
|
+
const n = Number.parseInt(req.params.n, 10);
|
|
56616
|
+
const p = Number.isInteger(n) ? snapshotPathFor(req.params.id, n) : null;
|
|
56617
|
+
if (!p || !(0, import_node_fs18.existsSync)(p)) return reply.status(404).send({ ok: false, error: "snapshot not found" });
|
|
56618
|
+
const ext = p.split(".").pop().toLowerCase();
|
|
56619
|
+
reply.header("Content-Type", ext === "png" ? "image/png" : ext === "webp" ? "image/webp" : "image/jpeg");
|
|
56620
|
+
reply.header("Cache-Control", "no-store");
|
|
56621
|
+
return (0, import_node_fs18.readFileSync)(p);
|
|
56622
|
+
});
|
|
56623
|
+
app.post(
|
|
56624
|
+
"/api/tweak",
|
|
56625
|
+
{ bodyLimit: 25 * 1024 * 1024 },
|
|
56626
|
+
async (req, reply) => {
|
|
56627
|
+
const { appId, nodeId, instruction, snapshots } = req.body ?? {};
|
|
56628
|
+
if (!appId || !nodeId || !instruction) {
|
|
56629
|
+
return reply.status(400).send({ ok: false, error: "appId, nodeId, instruction required" });
|
|
56630
|
+
}
|
|
56631
|
+
let decoded;
|
|
56632
|
+
try {
|
|
56633
|
+
decoded = decodeSnapshots(snapshots);
|
|
56634
|
+
} catch (e) {
|
|
56635
|
+
return reply.status(400).send({ ok: false, error: e instanceof Error ? e.message : "bad snapshot" });
|
|
56636
|
+
}
|
|
56637
|
+
const request = addRequest({ type: "tweak", appId, nodeId, instruction }, decoded);
|
|
56638
|
+
broadcast({ type: "request-added", request });
|
|
56639
|
+
return { ok: true, request };
|
|
56662
56640
|
}
|
|
56663
|
-
|
|
56664
|
-
|
|
56665
|
-
|
|
56666
|
-
|
|
56667
|
-
|
|
56668
|
-
|
|
56641
|
+
);
|
|
56642
|
+
app.post("/api/use-template", async (req, reply) => {
|
|
56643
|
+
const { appId, templateId } = req.body ?? {};
|
|
56644
|
+
if (!appId || !templateId) return reply.status(400).send({ ok: false, error: "appId and templateId required" });
|
|
56645
|
+
const template = getTemplate(templateId);
|
|
56646
|
+
if (!template) return reply.status(404).send({ ok: false, error: "template not found" });
|
|
56647
|
+
const request = addRequest({ type: "use-template", appId, templateId, template });
|
|
56648
|
+
broadcast({ type: "request-added", request });
|
|
56649
|
+
return { ok: true, request };
|
|
56650
|
+
});
|
|
56651
|
+
app.delete("/api/requests/:id", async (req, reply) => {
|
|
56652
|
+
if (!deleteRequest(req.params.id)) return reply.status(404).send({ ok: false, error: "request not found" });
|
|
56653
|
+
broadcast({ type: "requests-changed" });
|
|
56654
|
+
return { ok: true };
|
|
56655
|
+
});
|
|
56656
|
+
app.delete("/api/requests", async () => {
|
|
56657
|
+
const cleared = clearRequests();
|
|
56658
|
+
broadcast({ type: "requests-changed" });
|
|
56659
|
+
return { ok: true, cleared };
|
|
56660
|
+
});
|
|
56661
|
+
app.get("/api/events", async (_req, reply) => {
|
|
56662
|
+
reply.hijack();
|
|
56663
|
+
reply.raw.writeHead(200, {
|
|
56664
|
+
"Content-Type": "text/event-stream",
|
|
56665
|
+
"Cache-Control": "no-cache",
|
|
56666
|
+
Connection: "keep-alive"
|
|
56667
|
+
});
|
|
56668
|
+
reply.raw.write(`data: ${JSON.stringify({ type: "connected" })}
|
|
56669
|
+
|
|
56670
|
+
`);
|
|
56671
|
+
addClient(reply);
|
|
56672
|
+
});
|
|
56673
|
+
const watcher = startWatcher();
|
|
56674
|
+
runBootstrap(bootstrapDeps).then((st) => {
|
|
56675
|
+
if (st.status === "ready") startScheduler();
|
|
56676
|
+
else app.log.warn({ reason: st.reason }, "AWARE bootstrap not ready; scheduler deferred");
|
|
56677
|
+
}).catch((err) => app.log.error({ err: String(err) }, "bootstrap continuation failed"));
|
|
56678
|
+
const close = async () => {
|
|
56679
|
+
cancelAllConnects();
|
|
56680
|
+
clearAllStages();
|
|
56681
|
+
await stopScheduler();
|
|
56682
|
+
await watcher?.close();
|
|
56683
|
+
await app.close();
|
|
56684
|
+
process.exit(0);
|
|
56685
|
+
};
|
|
56686
|
+
process.on("SIGINT", close);
|
|
56687
|
+
process.on("SIGTERM", close);
|
|
56688
|
+
await app.listen({ port: PORT2, host: HOST });
|
|
56689
|
+
app.log.info(`floless.app on http://${HOST}:${PORT2} (aware npm ${getBootstrapState().awareVersion ?? "unresolved"})`);
|
|
56690
|
+
try {
|
|
56691
|
+
const synced = syncSkills().filter((r) => r.action === "installed" || r.action === "updated");
|
|
56692
|
+
if (synced.length) {
|
|
56693
|
+
app.log.info(
|
|
56694
|
+
{ skills: synced.map((s) => `${s.runtime}:${s.skill}@${s.to}`) },
|
|
56695
|
+
`synced ${synced.length} floless.app skill(s) into AI terminal(s)`
|
|
56696
|
+
);
|
|
56669
56697
|
}
|
|
56670
|
-
}
|
|
56671
|
-
log(
|
|
56672
|
-
}
|
|
56673
|
-
log("done. Finally, remove floless.app itself with: npm uninstall -g @floless/app");
|
|
56674
|
-
}
|
|
56675
|
-
var ACTIONS = { open: cmdOpen, start: cmdStart, supervise: cmdSupervise, stop: cmdStop, restart: cmdRestart, login: cmdLogin, update: cmdUpdate, uninstall: cmdUninstall };
|
|
56676
|
-
async function runAction(arg, flagArgv = [], selfVersion = null) {
|
|
56677
|
-
_selfVersion = selfVersion;
|
|
56678
|
-
const cmd = parseAction(arg);
|
|
56679
|
-
const action = ACTIONS[cmd];
|
|
56680
|
-
if (!action) {
|
|
56681
|
-
log(`unknown command "${cmd}" \u2014 use: open | start | supervise | stop | restart | login | update | uninstall`);
|
|
56682
|
-
process.exit(2);
|
|
56698
|
+
} catch (err) {
|
|
56699
|
+
app.log.warn({ err: String(err) }, "skill sync failed (non-fatal)");
|
|
56683
56700
|
}
|
|
56684
|
-
await action(parseTeardownFlags(flagArgv));
|
|
56685
|
-
}
|
|
56686
|
-
var entry = (0, import_node_path17.basename)(process.argv[1] ?? "").toLowerCase();
|
|
56687
|
-
if (entry === "launch.mjs") {
|
|
56688
|
-
runAction(process.argv[2], process.argv.slice(3)).catch((e) => {
|
|
56689
|
-
log(`error: ${e?.message ?? e}`);
|
|
56690
|
-
process.exit(1);
|
|
56691
|
-
});
|
|
56692
56701
|
}
|
|
56693
56702
|
|
|
56694
56703
|
// protocol.ts
|
|
@@ -56816,7 +56825,7 @@ async function main() {
|
|
|
56816
56825
|
`);
|
|
56817
56826
|
return;
|
|
56818
56827
|
}
|
|
56819
|
-
const result = await applyUpdate(check, { onBeforeApply: () =>
|
|
56828
|
+
const result = await applyUpdate(check, { onBeforeApply: () => cmdTeardown() });
|
|
56820
56829
|
process.stdout.write(`floless: ${result.message}
|
|
56821
56830
|
`);
|
|
56822
56831
|
process.exit(result.applied ? 0 : 1);
|