@floless/app 0.12.0 → 0.12.2
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 +196 -135
- package/dist/web/app.css +19 -0
- package/dist/web/aware.js +76 -5
- package/dist/web/index.html +2 -1
- package/launch.mjs +11 -1
- package/package.json +1 -1
package/dist/floless-server.cjs
CHANGED
|
@@ -5489,7 +5489,7 @@ var require_thread_stream = __commonJS({
|
|
|
5489
5489
|
var { version } = require_package();
|
|
5490
5490
|
var { EventEmitter: EventEmitter2 } = require("events");
|
|
5491
5491
|
var { Worker } = require("worker_threads");
|
|
5492
|
-
var { join:
|
|
5492
|
+
var { join: join22 } = require("path");
|
|
5493
5493
|
var { pathToFileURL } = require("url");
|
|
5494
5494
|
var { wait } = require_wait();
|
|
5495
5495
|
var {
|
|
@@ -5540,7 +5540,7 @@ var require_thread_stream = __commonJS({
|
|
|
5540
5540
|
function createWorker(stream, opts) {
|
|
5541
5541
|
const { filename, workerData } = opts;
|
|
5542
5542
|
const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
|
|
5543
|
-
const toExecute = bundlerOverrides["thread-stream-worker"] ||
|
|
5543
|
+
const toExecute = bundlerOverrides["thread-stream-worker"] || join22(__dirname, "lib", "worker.js");
|
|
5544
5544
|
const worker = new Worker(toExecute, {
|
|
5545
5545
|
...opts.workerOpts,
|
|
5546
5546
|
name: opts.workerOpts?.name || "thread-stream",
|
|
@@ -6006,9 +6006,9 @@ var require_transport = __commonJS({
|
|
|
6006
6006
|
"node_modules/pino/lib/transport.js"(exports2, module2) {
|
|
6007
6007
|
"use strict";
|
|
6008
6008
|
var { createRequire: createRequire4 } = require("module");
|
|
6009
|
-
var { existsSync:
|
|
6009
|
+
var { existsSync: existsSync20 } = require("node:fs");
|
|
6010
6010
|
var getCallers = require_caller();
|
|
6011
|
-
var { join:
|
|
6011
|
+
var { join: join22, isAbsolute: isAbsolute2, sep: sep3 } = require("node:path");
|
|
6012
6012
|
var { fileURLToPath: fileURLToPath4 } = require("node:url");
|
|
6013
6013
|
var sleep = require_atomic_sleep();
|
|
6014
6014
|
var onExit = require_on_exit_leak_free();
|
|
@@ -6080,7 +6080,7 @@ var require_transport = __commonJS({
|
|
|
6080
6080
|
return false;
|
|
6081
6081
|
}
|
|
6082
6082
|
}
|
|
6083
|
-
return isAbsolute2(path) && !
|
|
6083
|
+
return isAbsolute2(path) && !existsSync20(path);
|
|
6084
6084
|
}
|
|
6085
6085
|
function stripQuotes(value) {
|
|
6086
6086
|
const first = value[0];
|
|
@@ -6161,7 +6161,7 @@ var require_transport = __commonJS({
|
|
|
6161
6161
|
throw new Error("only one of target or targets can be specified");
|
|
6162
6162
|
}
|
|
6163
6163
|
if (targets) {
|
|
6164
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
6164
|
+
target = bundlerOverrides["pino-worker"] || join22(__dirname, "worker.js");
|
|
6165
6165
|
options.targets = targets.filter((dest) => dest.target).map((dest) => {
|
|
6166
6166
|
return {
|
|
6167
6167
|
...dest,
|
|
@@ -6179,7 +6179,7 @@ var require_transport = __commonJS({
|
|
|
6179
6179
|
});
|
|
6180
6180
|
});
|
|
6181
6181
|
} else if (pipeline2) {
|
|
6182
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
6182
|
+
target = bundlerOverrides["pino-worker"] || join22(__dirname, "worker.js");
|
|
6183
6183
|
options.pipelines = [pipeline2.map((dest) => {
|
|
6184
6184
|
return {
|
|
6185
6185
|
...dest,
|
|
@@ -6202,7 +6202,7 @@ var require_transport = __commonJS({
|
|
|
6202
6202
|
return origin;
|
|
6203
6203
|
}
|
|
6204
6204
|
if (origin === "pino/file") {
|
|
6205
|
-
return
|
|
6205
|
+
return join22(__dirname, "..", "file.js");
|
|
6206
6206
|
}
|
|
6207
6207
|
let fixTarget2;
|
|
6208
6208
|
for (const filePath of callers) {
|
|
@@ -7182,7 +7182,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
7182
7182
|
return circularValue;
|
|
7183
7183
|
}
|
|
7184
7184
|
let res = "";
|
|
7185
|
-
let
|
|
7185
|
+
let join22 = ",";
|
|
7186
7186
|
const originalIndentation = indentation;
|
|
7187
7187
|
if (Array.isArray(value)) {
|
|
7188
7188
|
if (value.length === 0) {
|
|
@@ -7196,7 +7196,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
7196
7196
|
indentation += spacer;
|
|
7197
7197
|
res += `
|
|
7198
7198
|
${indentation}`;
|
|
7199
|
-
|
|
7199
|
+
join22 = `,
|
|
7200
7200
|
${indentation}`;
|
|
7201
7201
|
}
|
|
7202
7202
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -7204,13 +7204,13 @@ ${indentation}`;
|
|
|
7204
7204
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
7205
7205
|
const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
7206
7206
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
7207
|
-
res +=
|
|
7207
|
+
res += join22;
|
|
7208
7208
|
}
|
|
7209
7209
|
const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
7210
7210
|
res += tmp !== void 0 ? tmp : "null";
|
|
7211
7211
|
if (value.length - 1 > maximumBreadth) {
|
|
7212
7212
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
7213
|
-
res += `${
|
|
7213
|
+
res += `${join22}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
7214
7214
|
}
|
|
7215
7215
|
if (spacer !== "") {
|
|
7216
7216
|
res += `
|
|
@@ -7231,7 +7231,7 @@ ${originalIndentation}`;
|
|
|
7231
7231
|
let separator = "";
|
|
7232
7232
|
if (spacer !== "") {
|
|
7233
7233
|
indentation += spacer;
|
|
7234
|
-
|
|
7234
|
+
join22 = `,
|
|
7235
7235
|
${indentation}`;
|
|
7236
7236
|
whitespace = " ";
|
|
7237
7237
|
}
|
|
@@ -7245,13 +7245,13 @@ ${indentation}`;
|
|
|
7245
7245
|
const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
|
|
7246
7246
|
if (tmp !== void 0) {
|
|
7247
7247
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
7248
|
-
separator =
|
|
7248
|
+
separator = join22;
|
|
7249
7249
|
}
|
|
7250
7250
|
}
|
|
7251
7251
|
if (keyLength > maximumBreadth) {
|
|
7252
7252
|
const removedKeys = keyLength - maximumBreadth;
|
|
7253
7253
|
res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
|
|
7254
|
-
separator =
|
|
7254
|
+
separator = join22;
|
|
7255
7255
|
}
|
|
7256
7256
|
if (spacer !== "" && separator.length > 1) {
|
|
7257
7257
|
res = `
|
|
@@ -7292,7 +7292,7 @@ ${originalIndentation}`;
|
|
|
7292
7292
|
}
|
|
7293
7293
|
const originalIndentation = indentation;
|
|
7294
7294
|
let res = "";
|
|
7295
|
-
let
|
|
7295
|
+
let join22 = ",";
|
|
7296
7296
|
if (Array.isArray(value)) {
|
|
7297
7297
|
if (value.length === 0) {
|
|
7298
7298
|
return "[]";
|
|
@@ -7305,7 +7305,7 @@ ${originalIndentation}`;
|
|
|
7305
7305
|
indentation += spacer;
|
|
7306
7306
|
res += `
|
|
7307
7307
|
${indentation}`;
|
|
7308
|
-
|
|
7308
|
+
join22 = `,
|
|
7309
7309
|
${indentation}`;
|
|
7310
7310
|
}
|
|
7311
7311
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -7313,13 +7313,13 @@ ${indentation}`;
|
|
|
7313
7313
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
7314
7314
|
const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
7315
7315
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
7316
|
-
res +=
|
|
7316
|
+
res += join22;
|
|
7317
7317
|
}
|
|
7318
7318
|
const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
7319
7319
|
res += tmp !== void 0 ? tmp : "null";
|
|
7320
7320
|
if (value.length - 1 > maximumBreadth) {
|
|
7321
7321
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
7322
|
-
res += `${
|
|
7322
|
+
res += `${join22}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
7323
7323
|
}
|
|
7324
7324
|
if (spacer !== "") {
|
|
7325
7325
|
res += `
|
|
@@ -7332,7 +7332,7 @@ ${originalIndentation}`;
|
|
|
7332
7332
|
let whitespace = "";
|
|
7333
7333
|
if (spacer !== "") {
|
|
7334
7334
|
indentation += spacer;
|
|
7335
|
-
|
|
7335
|
+
join22 = `,
|
|
7336
7336
|
${indentation}`;
|
|
7337
7337
|
whitespace = " ";
|
|
7338
7338
|
}
|
|
@@ -7341,7 +7341,7 @@ ${indentation}`;
|
|
|
7341
7341
|
const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
|
|
7342
7342
|
if (tmp !== void 0) {
|
|
7343
7343
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
7344
|
-
separator =
|
|
7344
|
+
separator = join22;
|
|
7345
7345
|
}
|
|
7346
7346
|
}
|
|
7347
7347
|
if (spacer !== "" && separator.length > 1) {
|
|
@@ -7399,20 +7399,20 @@ ${originalIndentation}`;
|
|
|
7399
7399
|
indentation += spacer;
|
|
7400
7400
|
let res2 = `
|
|
7401
7401
|
${indentation}`;
|
|
7402
|
-
const
|
|
7402
|
+
const join23 = `,
|
|
7403
7403
|
${indentation}`;
|
|
7404
7404
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
7405
7405
|
let i = 0;
|
|
7406
7406
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
7407
7407
|
const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
7408
7408
|
res2 += tmp2 !== void 0 ? tmp2 : "null";
|
|
7409
|
-
res2 +=
|
|
7409
|
+
res2 += join23;
|
|
7410
7410
|
}
|
|
7411
7411
|
const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
7412
7412
|
res2 += tmp !== void 0 ? tmp : "null";
|
|
7413
7413
|
if (value.length - 1 > maximumBreadth) {
|
|
7414
7414
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
7415
|
-
res2 += `${
|
|
7415
|
+
res2 += `${join23}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
7416
7416
|
}
|
|
7417
7417
|
res2 += `
|
|
7418
7418
|
${originalIndentation}`;
|
|
@@ -7428,16 +7428,16 @@ ${originalIndentation}`;
|
|
|
7428
7428
|
return '"[Object]"';
|
|
7429
7429
|
}
|
|
7430
7430
|
indentation += spacer;
|
|
7431
|
-
const
|
|
7431
|
+
const join22 = `,
|
|
7432
7432
|
${indentation}`;
|
|
7433
7433
|
let res = "";
|
|
7434
7434
|
let separator = "";
|
|
7435
7435
|
let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
|
|
7436
7436
|
if (isTypedArrayWithEntries(value)) {
|
|
7437
|
-
res += stringifyTypedArray(value,
|
|
7437
|
+
res += stringifyTypedArray(value, join22, maximumBreadth);
|
|
7438
7438
|
keys = keys.slice(value.length);
|
|
7439
7439
|
maximumPropertiesToStringify -= value.length;
|
|
7440
|
-
separator =
|
|
7440
|
+
separator = join22;
|
|
7441
7441
|
}
|
|
7442
7442
|
if (deterministic) {
|
|
7443
7443
|
keys = sort(keys, comparator);
|
|
@@ -7448,13 +7448,13 @@ ${indentation}`;
|
|
|
7448
7448
|
const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
|
|
7449
7449
|
if (tmp !== void 0) {
|
|
7450
7450
|
res += `${separator}${strEscape(key2)}: ${tmp}`;
|
|
7451
|
-
separator =
|
|
7451
|
+
separator = join22;
|
|
7452
7452
|
}
|
|
7453
7453
|
}
|
|
7454
7454
|
if (keyLength > maximumBreadth) {
|
|
7455
7455
|
const removedKeys = keyLength - maximumBreadth;
|
|
7456
7456
|
res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
|
|
7457
|
-
separator =
|
|
7457
|
+
separator = join22;
|
|
7458
7458
|
}
|
|
7459
7459
|
if (separator !== "") {
|
|
7460
7460
|
res = `
|
|
@@ -41991,7 +41991,7 @@ var require_send = __commonJS({
|
|
|
41991
41991
|
var { parseTokenList } = require_parseTokenList();
|
|
41992
41992
|
var { createHttpError } = require_createHttpError();
|
|
41993
41993
|
var extname2 = path.extname;
|
|
41994
|
-
var
|
|
41994
|
+
var join22 = path.join;
|
|
41995
41995
|
var normalize2 = path.normalize;
|
|
41996
41996
|
var resolve5 = path.resolve;
|
|
41997
41997
|
var sep3 = path.sep;
|
|
@@ -42078,7 +42078,7 @@ var require_send = __commonJS({
|
|
|
42078
42078
|
return { statusCode: 403 };
|
|
42079
42079
|
}
|
|
42080
42080
|
parts = path2.split(sep3);
|
|
42081
|
-
path2 = normalize2(
|
|
42081
|
+
path2 = normalize2(join22(root, path2));
|
|
42082
42082
|
} else {
|
|
42083
42083
|
if (UP_PATH_REGEXP.test(path2)) {
|
|
42084
42084
|
debug('malicious path "%s"', path2);
|
|
@@ -42361,7 +42361,7 @@ var require_send = __commonJS({
|
|
|
42361
42361
|
let err;
|
|
42362
42362
|
for (let i = 0; i < options.index.length; i++) {
|
|
42363
42363
|
const index = options.index[i];
|
|
42364
|
-
const p =
|
|
42364
|
+
const p = join22(path2, index);
|
|
42365
42365
|
const { error, stat: stat4 } = await tryStat(p);
|
|
42366
42366
|
if (error) {
|
|
42367
42367
|
err = error;
|
|
@@ -43012,7 +43012,7 @@ var require_static = __commonJS({
|
|
|
43012
43012
|
"use strict";
|
|
43013
43013
|
var path = require("node:path");
|
|
43014
43014
|
var { fileURLToPath: fileURLToPath4 } = require("node:url");
|
|
43015
|
-
var { statSync:
|
|
43015
|
+
var { statSync: statSync8 } = require("node:fs");
|
|
43016
43016
|
var { glob } = require_commonjs6();
|
|
43017
43017
|
var fp = require_plugin2();
|
|
43018
43018
|
var send = require_send2();
|
|
@@ -43145,8 +43145,8 @@ var require_static = __commonJS({
|
|
|
43145
43145
|
}
|
|
43146
43146
|
}
|
|
43147
43147
|
}
|
|
43148
|
-
for (const [
|
|
43149
|
-
const pathname =
|
|
43148
|
+
for (const [dirname10, rootPath] of indexDirs.entries()) {
|
|
43149
|
+
const pathname = dirname10 + (dirname10.endsWith("/") ? "" : "/");
|
|
43150
43150
|
const file = "/" + pathname.replace(prefix, "");
|
|
43151
43151
|
setUpHeadAndGet(routeOpts, pathname, file, rootPath);
|
|
43152
43152
|
if (opts.redirect === true) {
|
|
@@ -43375,7 +43375,7 @@ var require_static = __commonJS({
|
|
|
43375
43375
|
}
|
|
43376
43376
|
let pathStat;
|
|
43377
43377
|
try {
|
|
43378
|
-
pathStat =
|
|
43378
|
+
pathStat = statSync8(rootPath);
|
|
43379
43379
|
} catch (e) {
|
|
43380
43380
|
if (e.code === "ENOENT") {
|
|
43381
43381
|
fastify.log.warn(`"root" path "${rootPath}" must exist`);
|
|
@@ -43399,7 +43399,7 @@ var require_static = __commonJS({
|
|
|
43399
43399
|
return indexFiles.find((filename) => {
|
|
43400
43400
|
const p = path.join(root, pathname, filename);
|
|
43401
43401
|
try {
|
|
43402
|
-
const stats =
|
|
43402
|
+
const stats = statSync8(p);
|
|
43403
43403
|
return !stats.isDirectory();
|
|
43404
43404
|
} catch {
|
|
43405
43405
|
return false;
|
|
@@ -50827,9 +50827,9 @@ var import_node_readline2 = require("node:readline");
|
|
|
50827
50827
|
|
|
50828
50828
|
// index.ts
|
|
50829
50829
|
var import_node_url3 = require("node:url");
|
|
50830
|
-
var
|
|
50831
|
-
var
|
|
50832
|
-
var
|
|
50830
|
+
var import_node_path20 = require("node:path");
|
|
50831
|
+
var import_node_os14 = require("node:os");
|
|
50832
|
+
var import_node_fs21 = require("node:fs");
|
|
50833
50833
|
var import_node_child_process6 = require("node:child_process");
|
|
50834
50834
|
|
|
50835
50835
|
// log.mjs
|
|
@@ -52609,7 +52609,7 @@ function appVersion() {
|
|
|
52609
52609
|
return resolveVersion({
|
|
52610
52610
|
isSea: isSea2(),
|
|
52611
52611
|
sqVersionXml: readSqVersionXml(),
|
|
52612
|
-
define: true ? "0.12.
|
|
52612
|
+
define: true ? "0.12.2" : void 0,
|
|
52613
52613
|
pkgVersion: readPkgVersion()
|
|
52614
52614
|
});
|
|
52615
52615
|
}
|
|
@@ -52619,7 +52619,7 @@ function resolveChannel(s) {
|
|
|
52619
52619
|
return "dev";
|
|
52620
52620
|
}
|
|
52621
52621
|
function appChannel() {
|
|
52622
|
-
return resolveChannel({ isSea: isSea2(), define: true ? "0.12.
|
|
52622
|
+
return resolveChannel({ isSea: isSea2(), define: true ? "0.12.2" : void 0 });
|
|
52623
52623
|
}
|
|
52624
52624
|
|
|
52625
52625
|
// oauth-presets.ts
|
|
@@ -52964,6 +52964,13 @@ function writeTemplates(list) {
|
|
|
52964
52964
|
(0, import_node_fs12.renameSync)(tmp, TEMPLATES_FILE);
|
|
52965
52965
|
}
|
|
52966
52966
|
function addTemplate(input) {
|
|
52967
|
+
const list = listTemplates();
|
|
52968
|
+
if (!input.source?.nodeId && input.node.agent != null && input.node.command != null) {
|
|
52969
|
+
const dup = list.find(
|
|
52970
|
+
(t) => !t.source?.nodeId && t.node.agent === input.node.agent && t.node.command === input.node.command && t.node.kind === input.node.kind
|
|
52971
|
+
);
|
|
52972
|
+
if (dup) return dup;
|
|
52973
|
+
}
|
|
52967
52974
|
const tpl = {
|
|
52968
52975
|
id: (0, import_node_crypto4.randomUUID)(),
|
|
52969
52976
|
name: input.name.trim(),
|
|
@@ -52972,7 +52979,6 @@ function addTemplate(input) {
|
|
|
52972
52979
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
52973
52980
|
source: input.source
|
|
52974
52981
|
};
|
|
52975
|
-
const list = listTemplates();
|
|
52976
52982
|
list.push(tpl);
|
|
52977
52983
|
writeTemplates(list);
|
|
52978
52984
|
return tpl;
|
|
@@ -54026,10 +54032,58 @@ function unregisterAutostart() {
|
|
|
54026
54032
|
// updater.ts
|
|
54027
54033
|
var import_node_child_process4 = require("node:child_process");
|
|
54028
54034
|
var import_node_crypto5 = require("node:crypto");
|
|
54029
|
-
var
|
|
54035
|
+
var import_node_fs17 = require("node:fs");
|
|
54030
54036
|
var import_node_stream = require("node:stream");
|
|
54031
54037
|
var import_promises = require("node:stream/promises");
|
|
54038
|
+
var import_node_path15 = require("node:path");
|
|
54039
|
+
|
|
54040
|
+
// post-update-marker.mjs
|
|
54041
|
+
var import_node_fs16 = require("node:fs");
|
|
54042
|
+
var import_node_os11 = require("node:os");
|
|
54032
54043
|
var import_node_path14 = require("node:path");
|
|
54044
|
+
var FRESH_MS = 12e4;
|
|
54045
|
+
function markerPath() {
|
|
54046
|
+
const override = (process.env.FLOLESS_POST_UPDATE_MARKER ?? "").trim();
|
|
54047
|
+
if (override) return override;
|
|
54048
|
+
const root = process.env.FLOLESS_HOME ?? (0, import_node_path14.join)((0, import_node_os11.homedir)(), ".floless");
|
|
54049
|
+
return (0, import_node_path14.join)(root, ".post-update");
|
|
54050
|
+
}
|
|
54051
|
+
function legacyMarkerPath() {
|
|
54052
|
+
if ((process.env.FLOLESS_POST_UPDATE_MARKER ?? "").trim()) return null;
|
|
54053
|
+
try {
|
|
54054
|
+
return (0, import_node_path14.join)((0, import_node_path14.dirname)((0, import_node_path14.dirname)(process.execPath)), ".floless-post-update");
|
|
54055
|
+
} catch {
|
|
54056
|
+
return null;
|
|
54057
|
+
}
|
|
54058
|
+
}
|
|
54059
|
+
function writePostUpdateMarker() {
|
|
54060
|
+
let wrote = false;
|
|
54061
|
+
for (const p of [markerPath(), legacyMarkerPath()]) {
|
|
54062
|
+
if (!p) continue;
|
|
54063
|
+
try {
|
|
54064
|
+
(0, import_node_fs16.writeFileSync)(p, (/* @__PURE__ */ new Date()).toISOString());
|
|
54065
|
+
wrote = true;
|
|
54066
|
+
} catch {
|
|
54067
|
+
}
|
|
54068
|
+
}
|
|
54069
|
+
return wrote;
|
|
54070
|
+
}
|
|
54071
|
+
function consumePostUpdateMarker() {
|
|
54072
|
+
let fresh = false;
|
|
54073
|
+
for (const p of [markerPath(), legacyMarkerPath()]) {
|
|
54074
|
+
if (!p) continue;
|
|
54075
|
+
try {
|
|
54076
|
+
if (!(0, import_node_fs16.existsSync)(p)) continue;
|
|
54077
|
+
const ageMs = Date.now() - (0, import_node_fs16.statSync)(p).mtimeMs;
|
|
54078
|
+
(0, import_node_fs16.rmSync)(p, { force: true });
|
|
54079
|
+
if (ageMs < FRESH_MS) fresh = true;
|
|
54080
|
+
} catch {
|
|
54081
|
+
}
|
|
54082
|
+
}
|
|
54083
|
+
return fresh;
|
|
54084
|
+
}
|
|
54085
|
+
|
|
54086
|
+
// updater.ts
|
|
54033
54087
|
var CHANNEL = "win";
|
|
54034
54088
|
var FEED_TIMEOUT_MS = 15e3;
|
|
54035
54089
|
var DOWNLOAD_TIMEOUT_MS = 3e5;
|
|
@@ -54038,13 +54092,13 @@ function currentVersion() {
|
|
|
54038
54092
|
return appVersion();
|
|
54039
54093
|
}
|
|
54040
54094
|
function installRoot() {
|
|
54041
|
-
return (0,
|
|
54095
|
+
return (0, import_node_path15.dirname)((0, import_node_path15.dirname)(process.execPath));
|
|
54042
54096
|
}
|
|
54043
54097
|
function updateExePath() {
|
|
54044
|
-
return (0,
|
|
54098
|
+
return (0, import_node_path15.join)(installRoot(), "Update.exe");
|
|
54045
54099
|
}
|
|
54046
54100
|
function packagesDir() {
|
|
54047
|
-
return (0,
|
|
54101
|
+
return (0, import_node_path15.join)(installRoot(), "packages");
|
|
54048
54102
|
}
|
|
54049
54103
|
function feedUrl() {
|
|
54050
54104
|
const env2 = (process.env.FLOLESS_UPDATE_URL ?? "").trim().replace(/\/+$/, "");
|
|
@@ -54137,22 +54191,22 @@ async function checkForUpdate() {
|
|
|
54137
54191
|
}
|
|
54138
54192
|
async function sha1OfFile(path) {
|
|
54139
54193
|
const hash = (0, import_node_crypto5.createHash)("sha1");
|
|
54140
|
-
await (0, import_promises.pipeline)((0,
|
|
54194
|
+
await (0, import_promises.pipeline)((0, import_node_fs17.createReadStream)(path), hash);
|
|
54141
54195
|
return hash.digest("hex").toUpperCase();
|
|
54142
54196
|
}
|
|
54143
54197
|
async function downloadPackage(asset) {
|
|
54144
54198
|
if (!NUPKG_NAME.test(asset.FileName)) throw new Error(`refusing suspicious package name: ${asset.FileName}`);
|
|
54145
54199
|
const want = asset.SHA1.toUpperCase();
|
|
54146
54200
|
const dir = packagesDir();
|
|
54147
|
-
(0,
|
|
54148
|
-
const dest = (0,
|
|
54149
|
-
if ((0,
|
|
54201
|
+
(0, import_node_fs17.mkdirSync)(dir, { recursive: true });
|
|
54202
|
+
const dest = (0, import_node_path15.join)(dir, asset.FileName);
|
|
54203
|
+
if ((0, import_node_fs17.existsSync)(dest) && await sha1OfFile(dest) === want) return dest;
|
|
54150
54204
|
const res = await authedFetch(`${feedUrl()}/${encodeURIComponent(asset.FileName)}`, {
|
|
54151
54205
|
redirect: "follow",
|
|
54152
54206
|
signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS)
|
|
54153
54207
|
});
|
|
54154
54208
|
if (!res.ok || !res.body) throw new Error(`download failed: HTTP ${res.status} for ${asset.FileName}`);
|
|
54155
|
-
await (0, import_promises.pipeline)(import_node_stream.Readable.fromWeb(res.body), (0,
|
|
54209
|
+
await (0, import_promises.pipeline)(import_node_stream.Readable.fromWeb(res.body), (0, import_node_fs17.createWriteStream)(dest));
|
|
54156
54210
|
const got = await sha1OfFile(dest);
|
|
54157
54211
|
if (got !== want) throw new Error(`SHA1 mismatch for ${asset.FileName}: feed=${want} got=${got}`);
|
|
54158
54212
|
return dest;
|
|
@@ -54161,7 +54215,7 @@ async function applyUpdate(check, opts) {
|
|
|
54161
54215
|
if (!check.supported) return { applied: false, message: check.reason ?? "auto-update not supported in this runtime" };
|
|
54162
54216
|
if (!check.updateAvailable || !check.asset) return { applied: false, message: check.reason ?? "no update available" };
|
|
54163
54217
|
const exe = updateExePath();
|
|
54164
|
-
if (!(0,
|
|
54218
|
+
if (!(0, import_node_fs17.existsSync)(exe)) {
|
|
54165
54219
|
return { applied: false, message: `Update.exe not found at ${exe} \u2014 is this a Velopack install?` };
|
|
54166
54220
|
}
|
|
54167
54221
|
const pkg = await downloadPackage(check.asset);
|
|
@@ -54179,6 +54233,7 @@ async function applyUpdate(check, opts) {
|
|
|
54179
54233
|
resolve5();
|
|
54180
54234
|
});
|
|
54181
54235
|
});
|
|
54236
|
+
writePostUpdateMarker();
|
|
54182
54237
|
return { applied: true, message: `updating to v${check.targetVersion}\u2026 the app will relaunch` };
|
|
54183
54238
|
}
|
|
54184
54239
|
|
|
@@ -54369,12 +54424,12 @@ function isTraceCorrupt(events) {
|
|
|
54369
54424
|
|
|
54370
54425
|
// launch.mjs
|
|
54371
54426
|
var import_node_child_process5 = require("node:child_process");
|
|
54372
|
-
var
|
|
54427
|
+
var import_node_path16 = require("node:path");
|
|
54373
54428
|
var import_node_url = require("node:url");
|
|
54374
|
-
var
|
|
54429
|
+
var import_node_fs18 = require("node:fs");
|
|
54375
54430
|
var import_node_http = __toESM(require("node:http"), 1);
|
|
54376
54431
|
var import_node_readline = require("node:readline");
|
|
54377
|
-
var __dirname2 = (0,
|
|
54432
|
+
var __dirname2 = (0, import_node_path16.dirname)((0, import_node_url.fileURLToPath)(__import_meta_url));
|
|
54378
54433
|
var PORT = Number(process.env.PORT ?? 4317);
|
|
54379
54434
|
var HEALTH_URL = `http://127.0.0.1:${PORT}/api/health`;
|
|
54380
54435
|
var BROWSER_URL = `http://floless.localhost:${PORT}`;
|
|
@@ -54446,8 +54501,8 @@ async function waitHealthy(timeoutMs = 3e4) {
|
|
|
54446
54501
|
function resolveServerStart() {
|
|
54447
54502
|
const packaged = /flolessapp\.exe$/i.test(process.execPath);
|
|
54448
54503
|
if (packaged) return { cmd: process.execPath, args: ["--serve"], shell: false };
|
|
54449
|
-
const bundle = (0,
|
|
54450
|
-
if ((0,
|
|
54504
|
+
const bundle = (0, import_node_path16.join)(__dirname2, "dist", "floless-server.cjs");
|
|
54505
|
+
if ((0, import_node_fs18.existsSync)(bundle)) return { cmd: process.execPath, args: [bundle, "--serve"], shell: false };
|
|
54451
54506
|
return { cmd: "npm", args: ["run", "start"], shell: isWin2 };
|
|
54452
54507
|
}
|
|
54453
54508
|
function startServerDetached() {
|
|
@@ -54495,6 +54550,7 @@ async function ensureServerUp() {
|
|
|
54495
54550
|
log("server up");
|
|
54496
54551
|
}
|
|
54497
54552
|
async function cmdOpen() {
|
|
54553
|
+
const postUpdate = consumePostUpdateMarker();
|
|
54498
54554
|
if (await ping()) {
|
|
54499
54555
|
const running = await probeVersion();
|
|
54500
54556
|
if (shouldTakeOver(running, _selfVersion)) {
|
|
@@ -54503,11 +54559,15 @@ async function cmdOpen() {
|
|
|
54503
54559
|
await new Promise((r) => setTimeout(r, 500));
|
|
54504
54560
|
await ensureServerUp();
|
|
54505
54561
|
} else {
|
|
54506
|
-
log(`already running${running ? ` (v${running})` : ""}
|
|
54562
|
+
log(`already running${running ? ` (v${running})` : ""}`);
|
|
54507
54563
|
}
|
|
54508
54564
|
} else {
|
|
54509
54565
|
await ensureServerUp();
|
|
54510
54566
|
}
|
|
54567
|
+
if (postUpdate) {
|
|
54568
|
+
log("post-update relaunch \u2014 server up; the existing tab reconnects via its health poll (no new tab)");
|
|
54569
|
+
return;
|
|
54570
|
+
}
|
|
54511
54571
|
log(`opening ${BROWSER_URL}`);
|
|
54512
54572
|
openBrowser2(BROWSER_URL);
|
|
54513
54573
|
}
|
|
@@ -54595,8 +54655,8 @@ function taskkillArgs(pid, { tree = true } = {}) {
|
|
|
54595
54655
|
}
|
|
54596
54656
|
function killSupervisor({ tree = true } = {}) {
|
|
54597
54657
|
if (!isWin2) return;
|
|
54598
|
-
const isNpmChannel = /^node(\.exe)?$/i.test((0,
|
|
54599
|
-
const scriptMatch = isNpmChannel ? (0,
|
|
54658
|
+
const isNpmChannel = /^node(\.exe)?$/i.test((0, import_node_path16.basename)(process.execPath));
|
|
54659
|
+
const scriptMatch = isNpmChannel ? (0, import_node_path16.basename)((0, import_node_url.fileURLToPath)(__import_meta_url)) : void 0;
|
|
54600
54660
|
const realExe = resolveRealInstallExe(process.execPath);
|
|
54601
54661
|
const exeMatch = realExe === process.execPath ? process.execPath : [process.execPath, realExe];
|
|
54602
54662
|
const pids = supervisorPidsToKill(enumerateProcesses(), process.pid, exeMatch, scriptMatch);
|
|
@@ -54761,7 +54821,7 @@ async function runAction(arg, flagArgv = [], selfVersion = null) {
|
|
|
54761
54821
|
}
|
|
54762
54822
|
await action(parseTeardownFlags(flagArgv));
|
|
54763
54823
|
}
|
|
54764
|
-
var entry = (0,
|
|
54824
|
+
var entry = (0, import_node_path16.basename)(process.argv[1] ?? "").toLowerCase();
|
|
54765
54825
|
if (entry === "launch.mjs") {
|
|
54766
54826
|
runAction(process.argv[2], process.argv.slice(3)).catch((e) => {
|
|
54767
54827
|
log(`error: ${e?.message ?? e}`);
|
|
@@ -54835,9 +54895,9 @@ function awareUpgradeBlockReason(s) {
|
|
|
54835
54895
|
}
|
|
54836
54896
|
|
|
54837
54897
|
// skill-sync.ts
|
|
54838
|
-
var
|
|
54839
|
-
var
|
|
54840
|
-
var
|
|
54898
|
+
var import_node_fs19 = require("node:fs");
|
|
54899
|
+
var import_node_os12 = require("node:os");
|
|
54900
|
+
var import_node_path17 = require("node:path");
|
|
54841
54901
|
var import_node_url2 = require("node:url");
|
|
54842
54902
|
var import_yaml5 = __toESM(require_dist6(), 1);
|
|
54843
54903
|
|
|
@@ -54862,30 +54922,30 @@ function selectShippedSkillNames(names) {
|
|
|
54862
54922
|
}
|
|
54863
54923
|
|
|
54864
54924
|
// skill-sync.ts
|
|
54865
|
-
var __dirname3 = (0,
|
|
54925
|
+
var __dirname3 = (0, import_node_path17.dirname)((0, import_node_url2.fileURLToPath)(__import_meta_url));
|
|
54866
54926
|
function bundledSkillsRoot() {
|
|
54867
54927
|
const candidates = [
|
|
54868
|
-
(0,
|
|
54869
|
-
(0,
|
|
54870
|
-
(0,
|
|
54928
|
+
(0, import_node_path17.join)(__dirname3, "skills"),
|
|
54929
|
+
(0, import_node_path17.join)((0, import_node_path17.dirname)(process.execPath), "skills"),
|
|
54930
|
+
(0, import_node_path17.join)(__dirname3, "..", ".claude", "skills")
|
|
54871
54931
|
];
|
|
54872
|
-
return candidates.find((p) => (0,
|
|
54932
|
+
return candidates.find((p) => (0, import_node_fs19.existsSync)(p)) ?? null;
|
|
54873
54933
|
}
|
|
54874
54934
|
function targetConfigDirs() {
|
|
54875
54935
|
const override = process.env.FLOLESS_SKILL_TARGETS;
|
|
54876
54936
|
if (override) {
|
|
54877
54937
|
return override.split(";").map((d) => d.trim()).filter(Boolean).map((dir) => ({ runtime: "custom", dir }));
|
|
54878
54938
|
}
|
|
54879
|
-
const home = (0,
|
|
54939
|
+
const home = (0, import_node_os12.homedir)();
|
|
54880
54940
|
return [
|
|
54881
|
-
{ runtime: "claude", dir: (0,
|
|
54882
|
-
{ runtime: "codex", dir: (0,
|
|
54883
|
-
{ runtime: "opencode", dir: (0,
|
|
54941
|
+
{ runtime: "claude", dir: (0, import_node_path17.join)(home, ".claude") },
|
|
54942
|
+
{ runtime: "codex", dir: (0, import_node_path17.join)(home, ".codex") },
|
|
54943
|
+
{ runtime: "opencode", dir: (0, import_node_path17.join)(home, ".opencode") }
|
|
54884
54944
|
];
|
|
54885
54945
|
}
|
|
54886
54946
|
function skillVersion(skillMdPath) {
|
|
54887
54947
|
try {
|
|
54888
|
-
const text = (0,
|
|
54948
|
+
const text = (0, import_node_fs19.readFileSync)(skillMdPath, "utf8");
|
|
54889
54949
|
const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(text);
|
|
54890
54950
|
if (!m || m[1] === void 0) return null;
|
|
54891
54951
|
const fm = (0, import_yaml5.parse)(m[1]);
|
|
@@ -54925,21 +54985,21 @@ function decideAction(installed, bundled) {
|
|
|
54925
54985
|
function bundledSkills(root) {
|
|
54926
54986
|
let entries = [];
|
|
54927
54987
|
try {
|
|
54928
|
-
entries = selectShippedSkillNames((0,
|
|
54988
|
+
entries = selectShippedSkillNames((0, import_node_fs19.readdirSync)(root));
|
|
54929
54989
|
} catch {
|
|
54930
54990
|
return [];
|
|
54931
54991
|
}
|
|
54932
54992
|
const out = [];
|
|
54933
54993
|
for (const name of entries) {
|
|
54934
|
-
const dir = (0,
|
|
54994
|
+
const dir = (0, import_node_path17.join)(root, name);
|
|
54935
54995
|
let isDir = false;
|
|
54936
54996
|
try {
|
|
54937
|
-
isDir = (0,
|
|
54997
|
+
isDir = (0, import_node_fs19.statSync)(dir).isDirectory();
|
|
54938
54998
|
} catch {
|
|
54939
54999
|
isDir = false;
|
|
54940
55000
|
}
|
|
54941
55001
|
if (!isDir) continue;
|
|
54942
|
-
const v = skillVersion((0,
|
|
55002
|
+
const v = skillVersion((0, import_node_path17.join)(dir, "SKILL.md"));
|
|
54943
55003
|
if (!v) continue;
|
|
54944
55004
|
out.push({ name, dir, version: v });
|
|
54945
55005
|
}
|
|
@@ -54952,17 +55012,17 @@ function syncSkills() {
|
|
|
54952
55012
|
const skills = bundledSkills(root);
|
|
54953
55013
|
if (!skills.length) return results;
|
|
54954
55014
|
for (const { runtime, dir: cfg } of targetConfigDirs()) {
|
|
54955
|
-
if (!(0,
|
|
54956
|
-
const skillsDir = (0,
|
|
55015
|
+
if (!(0, import_node_fs19.existsSync)(cfg)) continue;
|
|
55016
|
+
const skillsDir = (0, import_node_path17.join)(cfg, "skills");
|
|
54957
55017
|
for (const s of skills) {
|
|
54958
|
-
const dest = (0,
|
|
54959
|
-
const installedMd = (0,
|
|
54960
|
-
const installed = (0,
|
|
55018
|
+
const dest = (0, import_node_path17.join)(skillsDir, s.name);
|
|
55019
|
+
const installedMd = (0, import_node_path17.join)(dest, "SKILL.md");
|
|
55020
|
+
const installed = (0, import_node_fs19.existsSync)(installedMd) ? skillVersion(installedMd) : null;
|
|
54961
55021
|
const action = decideAction(installed, s.version);
|
|
54962
55022
|
if (action === "installed" || action === "updated") {
|
|
54963
55023
|
try {
|
|
54964
|
-
if (action === "updated") (0,
|
|
54965
|
-
(0,
|
|
55024
|
+
if (action === "updated") (0, import_node_fs19.rmSync)(dest, { recursive: true, force: true });
|
|
55025
|
+
(0, import_node_fs19.cpSync)(s.dir, dest, { recursive: true });
|
|
54966
55026
|
results.push({ runtime, skill: s.name, action, from: installed, to: s.version });
|
|
54967
55027
|
} catch {
|
|
54968
55028
|
}
|
|
@@ -54975,9 +55035,9 @@ function syncSkills() {
|
|
|
54975
55035
|
}
|
|
54976
55036
|
|
|
54977
55037
|
// watch.ts
|
|
54978
|
-
var
|
|
54979
|
-
var
|
|
54980
|
-
var
|
|
55038
|
+
var import_node_os13 = require("node:os");
|
|
55039
|
+
var import_node_path19 = require("node:path");
|
|
55040
|
+
var import_node_fs20 = require("node:fs");
|
|
54981
55041
|
|
|
54982
55042
|
// node_modules/chokidar/esm/index.js
|
|
54983
55043
|
var import_fs2 = require("fs");
|
|
@@ -54988,7 +55048,7 @@ var sysPath2 = __toESM(require("path"), 1);
|
|
|
54988
55048
|
// node_modules/readdirp/esm/index.js
|
|
54989
55049
|
var import_promises2 = require("node:fs/promises");
|
|
54990
55050
|
var import_node_stream2 = require("node:stream");
|
|
54991
|
-
var
|
|
55051
|
+
var import_node_path18 = require("node:path");
|
|
54992
55052
|
var EntryTypes = {
|
|
54993
55053
|
FILE_TYPE: "files",
|
|
54994
55054
|
DIR_TYPE: "directories",
|
|
@@ -55063,7 +55123,7 @@ var ReaddirpStream = class extends import_node_stream2.Readable {
|
|
|
55063
55123
|
this._wantsDir = type ? DIR_TYPES.has(type) : false;
|
|
55064
55124
|
this._wantsFile = type ? FILE_TYPES.has(type) : false;
|
|
55065
55125
|
this._wantsEverything = type === EntryTypes.EVERYTHING_TYPE;
|
|
55066
|
-
this._root = (0,
|
|
55126
|
+
this._root = (0, import_node_path18.resolve)(root);
|
|
55067
55127
|
this._isDirent = !opts.alwaysStat;
|
|
55068
55128
|
this._statsProp = this._isDirent ? "dirent" : "stats";
|
|
55069
55129
|
this._rdOptions = { encoding: "utf8", withFileTypes: this._isDirent };
|
|
@@ -55134,8 +55194,8 @@ var ReaddirpStream = class extends import_node_stream2.Readable {
|
|
|
55134
55194
|
let entry2;
|
|
55135
55195
|
const basename5 = this._isDirent ? dirent.name : dirent;
|
|
55136
55196
|
try {
|
|
55137
|
-
const fullPath = (0,
|
|
55138
|
-
entry2 = { path: (0,
|
|
55197
|
+
const fullPath = (0, import_node_path18.resolve)((0, import_node_path18.join)(path, basename5));
|
|
55198
|
+
entry2 = { path: (0, import_node_path18.relative)(this._root, fullPath), fullPath, basename: basename5 };
|
|
55139
55199
|
entry2[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
55140
55200
|
} catch (err) {
|
|
55141
55201
|
this._onError(err);
|
|
@@ -55169,7 +55229,7 @@ var ReaddirpStream = class extends import_node_stream2.Readable {
|
|
|
55169
55229
|
}
|
|
55170
55230
|
if (entryRealPathStats.isDirectory()) {
|
|
55171
55231
|
const len = entryRealPath.length;
|
|
55172
|
-
if (full.startsWith(entryRealPath) && full.substr(len, 1) ===
|
|
55232
|
+
if (full.startsWith(entryRealPath) && full.substr(len, 1) === import_node_path18.sep) {
|
|
55173
55233
|
const recursiveError = new Error(`Circular symlink detected: "${full}" points to "${entryRealPath}"`);
|
|
55174
55234
|
recursiveError.code = RECURSIVE_ERROR_CODE;
|
|
55175
55235
|
return this._onError(recursiveError);
|
|
@@ -55708,9 +55768,9 @@ var NodeFsHandler = class {
|
|
|
55708
55768
|
if (this.fsw.closed) {
|
|
55709
55769
|
return;
|
|
55710
55770
|
}
|
|
55711
|
-
const
|
|
55771
|
+
const dirname10 = sysPath.dirname(file);
|
|
55712
55772
|
const basename5 = sysPath.basename(file);
|
|
55713
|
-
const parent = this.fsw._getWatchedDir(
|
|
55773
|
+
const parent = this.fsw._getWatchedDir(dirname10);
|
|
55714
55774
|
let prevStats = stats;
|
|
55715
55775
|
if (parent.has(basename5))
|
|
55716
55776
|
return;
|
|
@@ -55737,7 +55797,7 @@ var NodeFsHandler = class {
|
|
|
55737
55797
|
prevStats = newStats2;
|
|
55738
55798
|
}
|
|
55739
55799
|
} catch (error) {
|
|
55740
|
-
this.fsw._remove(
|
|
55800
|
+
this.fsw._remove(dirname10, basename5);
|
|
55741
55801
|
}
|
|
55742
55802
|
} else if (parent.has(basename5)) {
|
|
55743
55803
|
const at = newStats.atimeMs;
|
|
@@ -56677,33 +56737,33 @@ function appIdFromLogPath(path) {
|
|
|
56677
56737
|
return i >= 0 && parts[i + 1] ? parts[i + 1] : null;
|
|
56678
56738
|
}
|
|
56679
56739
|
function samePath(a, b) {
|
|
56680
|
-
const ra = (0,
|
|
56681
|
-
const rb = (0,
|
|
56740
|
+
const ra = (0, import_node_path19.resolve)(a);
|
|
56741
|
+
const rb = (0, import_node_path19.resolve)(b);
|
|
56682
56742
|
return process.platform === "win32" ? ra.toLowerCase() === rb.toLowerCase() : ra === rb;
|
|
56683
56743
|
}
|
|
56684
56744
|
function underDir(path, dir) {
|
|
56685
|
-
const rp = (0,
|
|
56686
|
-
const rd = (0,
|
|
56745
|
+
const rp = (0, import_node_path19.resolve)(path);
|
|
56746
|
+
const rd = (0, import_node_path19.resolve)(dir);
|
|
56687
56747
|
const [p, d] = process.platform === "win32" ? [rp.toLowerCase(), rd.toLowerCase()] : [rp, rd];
|
|
56688
|
-
return p === d || p.startsWith(d +
|
|
56748
|
+
return p === d || p.startsWith(d + import_node_path19.sep);
|
|
56689
56749
|
}
|
|
56690
56750
|
function startWatcher() {
|
|
56691
|
-
const awareDir = process.env.AWARE_HOME ?? (0,
|
|
56692
|
-
const credentialsDir = (0,
|
|
56693
|
-
if (!(0,
|
|
56751
|
+
const awareDir = process.env.AWARE_HOME ?? (0, import_node_path19.join)((0, import_node_os13.homedir)(), ".aware");
|
|
56752
|
+
const credentialsDir = (0, import_node_path19.join)(awareDir, "credentials");
|
|
56753
|
+
if (!(0, import_node_fs20.existsSync)(credentialsDir)) {
|
|
56694
56754
|
try {
|
|
56695
|
-
(0,
|
|
56755
|
+
(0, import_node_fs20.mkdirSync)(credentialsDir, { recursive: true });
|
|
56696
56756
|
} catch {
|
|
56697
56757
|
}
|
|
56698
56758
|
}
|
|
56699
|
-
if (!(0,
|
|
56759
|
+
if (!(0, import_node_fs20.existsSync)(uiDir)) {
|
|
56700
56760
|
try {
|
|
56701
|
-
(0,
|
|
56761
|
+
(0, import_node_fs20.mkdirSync)(uiDir, { recursive: true });
|
|
56702
56762
|
} catch {
|
|
56703
56763
|
}
|
|
56704
56764
|
}
|
|
56705
|
-
const targets = ["apps", "logs", "credentials"].map((d) => (0,
|
|
56706
|
-
if ((0,
|
|
56765
|
+
const targets = ["apps", "logs", "credentials"].map((d) => (0, import_node_path19.join)(awareDir, d)).filter((p) => (0, import_node_fs20.existsSync)(p));
|
|
56766
|
+
if ((0, import_node_fs20.existsSync)(uiDir)) targets.push(uiDir);
|
|
56707
56767
|
if (targets.length === 0) {
|
|
56708
56768
|
return null;
|
|
56709
56769
|
}
|
|
@@ -56733,11 +56793,11 @@ function startWatcher() {
|
|
|
56733
56793
|
const isCredential = path.split(/[\\/]/).includes("credentials");
|
|
56734
56794
|
const kind = isCredential ? "credential" : path.endsWith(".jsonl") ? "trace" : path.endsWith(".lock") ? "lock" : path.endsWith(".flo") || path.endsWith(".app") ? "source" : "file";
|
|
56735
56795
|
broadcast({ type: "fs-change", kind, event, path });
|
|
56736
|
-
if (kind === "trace" && event !== "unlink" && (0,
|
|
56796
|
+
if (kind === "trace" && event !== "unlink" && (0, import_node_fs20.existsSync)(path)) {
|
|
56737
56797
|
const id = appIdFromLogPath(path);
|
|
56738
56798
|
if (!id) return;
|
|
56739
56799
|
try {
|
|
56740
|
-
broadcast({ type: "trace-file", id, runId: path.split(
|
|
56800
|
+
broadcast({ type: "trace-file", id, runId: path.split(import_node_path19.sep).pop()?.replace(/\.jsonl$/, "") ?? null, events: parseTrace((0, import_node_fs20.readFileSync)(path, "utf8")) });
|
|
56741
56801
|
} catch {
|
|
56742
56802
|
}
|
|
56743
56803
|
}
|
|
@@ -56746,10 +56806,10 @@ function startWatcher() {
|
|
|
56746
56806
|
}
|
|
56747
56807
|
|
|
56748
56808
|
// index.ts
|
|
56749
|
-
var __dirname4 = (0,
|
|
56750
|
-
var WEB_ROOT = [(0,
|
|
56751
|
-
(p) => (0,
|
|
56752
|
-
) ?? (0,
|
|
56809
|
+
var __dirname4 = (0, import_node_path20.dirname)((0, import_node_url3.fileURLToPath)(__import_meta_url));
|
|
56810
|
+
var WEB_ROOT = [(0, import_node_path20.join)(__dirname4, "web"), (0, import_node_path20.join)((0, import_node_path20.dirname)(process.execPath), "web"), (0, import_node_path20.join)(__dirname4, "..", "web")].find(
|
|
56811
|
+
(p) => (0, import_node_fs21.existsSync)(p)
|
|
56812
|
+
) ?? (0, import_node_path20.join)(__dirname4, "..", "web");
|
|
56753
56813
|
var PORT2 = Number(process.env.PORT ?? 4317);
|
|
56754
56814
|
var HOST = "127.0.0.1";
|
|
56755
56815
|
function extractReportHtml(events) {
|
|
@@ -56776,7 +56836,7 @@ function installCrashHandlers() {
|
|
|
56776
56836
|
${stack}
|
|
56777
56837
|
`;
|
|
56778
56838
|
try {
|
|
56779
|
-
(0,
|
|
56839
|
+
(0, import_node_fs21.appendFileSync)(logFilePath(), line);
|
|
56780
56840
|
} catch {
|
|
56781
56841
|
}
|
|
56782
56842
|
if (process.stderr.isTTY) process.stderr.write(line);
|
|
@@ -57121,13 +57181,13 @@ async function startServer() {
|
|
|
57121
57181
|
}
|
|
57122
57182
|
const inputs = appData.inputs.map((i) => ({ name: i.name, type: i.type }));
|
|
57123
57183
|
const baked = bakeFloSource(appData.source.text, inputs);
|
|
57124
|
-
const tmpRoot = (0,
|
|
57125
|
-
const backupDir = (0,
|
|
57126
|
-
const bakeDir = (0,
|
|
57127
|
-
(0,
|
|
57128
|
-
(0,
|
|
57184
|
+
const tmpRoot = (0, import_node_fs21.mkdtempSync)((0, import_node_path20.join)((0, import_node_os14.tmpdir)(), "floless-bake-"));
|
|
57185
|
+
const backupDir = (0, import_node_path20.join)(tmpRoot, `${id}-backup`);
|
|
57186
|
+
const bakeDir = (0, import_node_path20.join)(tmpRoot, id);
|
|
57187
|
+
(0, import_node_fs21.cpSync)(appDir(id), backupDir, { recursive: true });
|
|
57188
|
+
(0, import_node_fs21.cpSync)(appDir(id), bakeDir, { recursive: true });
|
|
57129
57189
|
const floName = appData.source.path.split(/[\\/]/).pop();
|
|
57130
|
-
(0,
|
|
57190
|
+
(0, import_node_fs21.writeFileSync)((0, import_node_path20.join)(bakeDir, floName), baked);
|
|
57131
57191
|
let appInstalled = true;
|
|
57132
57192
|
try {
|
|
57133
57193
|
await aware.uninstall(id);
|
|
@@ -57149,17 +57209,17 @@ async function startServer() {
|
|
|
57149
57209
|
throw installErr;
|
|
57150
57210
|
}
|
|
57151
57211
|
try {
|
|
57152
|
-
await aware.compile((0,
|
|
57212
|
+
await aware.compile((0, import_node_path20.join)(appDir(id), floName));
|
|
57153
57213
|
} catch (compileErr) {
|
|
57154
57214
|
app.log.warn({ id, compileErr: String(compileErr) }, "bake: post-install recompile failed (app baked but may need a manual Compile)");
|
|
57155
57215
|
}
|
|
57156
57216
|
broadcast({ type: "baked", id });
|
|
57157
57217
|
return { ok: true, id, agent: id, inputs };
|
|
57158
57218
|
} finally {
|
|
57159
|
-
if (appInstalled) (0,
|
|
57219
|
+
if (appInstalled) (0, import_node_fs21.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
57160
57220
|
}
|
|
57161
57221
|
});
|
|
57162
|
-
const graftAgentsDir = () => (0,
|
|
57222
|
+
const graftAgentsDir = () => (0, import_node_path20.join)((0, import_node_os14.homedir)(), ".aware", "agents");
|
|
57163
57223
|
app.post("/api/graft/match", async (req, reply) => {
|
|
57164
57224
|
const { glob } = req.body ?? {};
|
|
57165
57225
|
if (!glob) return reply.status(400).send({ ok: false, error: "glob required" });
|
|
@@ -57176,7 +57236,7 @@ async function startServer() {
|
|
|
57176
57236
|
if (!sourceKind || !sourceRef) {
|
|
57177
57237
|
return reply.status(400).send({ ok: false, error: "sourceKind and sourceRef required" });
|
|
57178
57238
|
}
|
|
57179
|
-
const tempHome = (0,
|
|
57239
|
+
const tempHome = (0, import_node_fs21.mkdtempSync)((0, import_node_path20.join)((0, import_node_os14.tmpdir)(), "floless-graft-"));
|
|
57180
57240
|
let result;
|
|
57181
57241
|
try {
|
|
57182
57242
|
result = await aware.build({
|
|
@@ -57189,19 +57249,19 @@ async function startServer() {
|
|
|
57189
57249
|
awareHome: tempHome
|
|
57190
57250
|
});
|
|
57191
57251
|
} catch (err) {
|
|
57192
|
-
(0,
|
|
57252
|
+
(0, import_node_fs21.rmSync)(tempHome, { recursive: true, force: true });
|
|
57193
57253
|
const msg = err instanceof AwareError ? err.message : String(err?.message ?? err);
|
|
57194
57254
|
return reply.status(422).send({ ok: false, error: msg });
|
|
57195
57255
|
}
|
|
57196
57256
|
const manifest = readStagedManifest(result.agentDir);
|
|
57197
57257
|
if (!manifest) {
|
|
57198
|
-
(0,
|
|
57258
|
+
(0, import_node_fs21.rmSync)(tempHome, { recursive: true, force: true });
|
|
57199
57259
|
return reply.status(502).send({ ok: false, error: `build produced output at ${result.agentDir} but no manifest.yaml` });
|
|
57200
57260
|
}
|
|
57201
57261
|
const token = (0, import_node_crypto6.randomUUID)();
|
|
57202
57262
|
registerStage(token, tempHome, result.agentId);
|
|
57203
57263
|
const preview = buildPreview(manifest, sourceKind, sourceRef, token);
|
|
57204
|
-
if ((0,
|
|
57264
|
+
if ((0, import_node_fs21.existsSync)((0, import_node_path20.join)(graftAgentsDir(), result.agentId))) {
|
|
57205
57265
|
preview.warnings.unshift(`An agent named "${result.agentId}" is already installed \u2014 creating it will overwrite it.`);
|
|
57206
57266
|
}
|
|
57207
57267
|
return { ok: true, preview };
|
|
@@ -57220,7 +57280,7 @@ async function startServer() {
|
|
|
57220
57280
|
registerStage(stagedRef, stage.tempDir, stage.agentId);
|
|
57221
57281
|
return reply.status(409).send({ ok: false, error: err.message, agentId: stage.agentId, collision: true });
|
|
57222
57282
|
}
|
|
57223
|
-
(0,
|
|
57283
|
+
(0, import_node_fs21.rmSync)(stage.tempDir, { recursive: true, force: true });
|
|
57224
57284
|
throw err;
|
|
57225
57285
|
}
|
|
57226
57286
|
broadcast({ type: "grafted", id: stage.agentId });
|
|
@@ -57394,11 +57454,11 @@ async function startServer() {
|
|
|
57394
57454
|
app.get("/api/requests/:id/snapshot/:n", async (req, reply) => {
|
|
57395
57455
|
const n = Number.parseInt(req.params.n, 10);
|
|
57396
57456
|
const p = Number.isInteger(n) ? snapshotPathFor(req.params.id, n) : null;
|
|
57397
|
-
if (!p || !(0,
|
|
57457
|
+
if (!p || !(0, import_node_fs21.existsSync)(p)) return reply.status(404).send({ ok: false, error: "snapshot not found" });
|
|
57398
57458
|
const ext = p.split(".").pop().toLowerCase();
|
|
57399
57459
|
reply.header("Content-Type", ext === "png" ? "image/png" : ext === "webp" ? "image/webp" : "image/jpeg");
|
|
57400
57460
|
reply.header("Cache-Control", "no-store");
|
|
57401
|
-
return (0,
|
|
57461
|
+
return (0, import_node_fs21.readFileSync)(p);
|
|
57402
57462
|
});
|
|
57403
57463
|
app.post(
|
|
57404
57464
|
"/api/tweak",
|
|
@@ -57492,7 +57552,7 @@ async function startServer() {
|
|
|
57492
57552
|
warn(`last-run-status:${ref} \u2014 trace exists but couldn't be parsed (corrupt/truncated)`);
|
|
57493
57553
|
let finishedAt2 = null;
|
|
57494
57554
|
try {
|
|
57495
|
-
finishedAt2 = (0,
|
|
57555
|
+
finishedAt2 = (0, import_node_fs21.statSync)(latest.path).mtime.toISOString();
|
|
57496
57556
|
} catch {
|
|
57497
57557
|
finishedAt2 = null;
|
|
57498
57558
|
}
|
|
@@ -57502,7 +57562,7 @@ async function startServer() {
|
|
|
57502
57562
|
let finishedAt = typeof runEnd?.ts === "string" ? runEnd.ts : null;
|
|
57503
57563
|
if (!finishedAt) {
|
|
57504
57564
|
try {
|
|
57505
|
-
finishedAt = (0,
|
|
57565
|
+
finishedAt = (0, import_node_fs21.statSync)(latest.path).mtime.toISOString();
|
|
57506
57566
|
} catch {
|
|
57507
57567
|
finishedAt = null;
|
|
57508
57568
|
}
|
|
@@ -57704,6 +57764,7 @@ async function main() {
|
|
|
57704
57764
|
const realExe = resolveRealInstallExe(process.execPath);
|
|
57705
57765
|
registerProtocol(realExe);
|
|
57706
57766
|
registerAutostart(realExe);
|
|
57767
|
+
if (veloHook === "--veloapp-updated") writePostUpdateMarker();
|
|
57707
57768
|
} else if (veloHook === "--veloapp-uninstall") {
|
|
57708
57769
|
unregisterProtocol();
|
|
57709
57770
|
unregisterAutostart();
|
package/dist/web/app.css
CHANGED
|
@@ -1935,6 +1935,25 @@ body {
|
|
|
1935
1935
|
.run-state.drift { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, transparent); }
|
|
1936
1936
|
.run-state.uncompiled { color: var(--text-muted); border-color: var(--border-strong); }
|
|
1937
1937
|
|
|
1938
|
+
/* Header Stop — the always-reachable escape hatch during a run, regardless of whether
|
|
1939
|
+
the HTML Viewer modal is open or closed (#39). Shown only while a run is in flight.
|
|
1940
|
+
Mirrors the in-modal .overlay-stop: a quiet destructive action — warn-tinted outline
|
|
1941
|
+
that fills on hover, never a primary. Inherits the header button's size/radius/font;
|
|
1942
|
+
only the colours are overridden here. */
|
|
1943
|
+
.run-stop-btn {
|
|
1944
|
+
background: transparent;
|
|
1945
|
+
color: var(--warn);
|
|
1946
|
+
border: 1px solid color-mix(in srgb, var(--warn) 45%, transparent);
|
|
1947
|
+
font-weight: 600;
|
|
1948
|
+
letter-spacing: 0.04em;
|
|
1949
|
+
}
|
|
1950
|
+
.run-stop-btn:hover {
|
|
1951
|
+
background: color-mix(in srgb, var(--warn) 16%, transparent);
|
|
1952
|
+
border-color: var(--warn);
|
|
1953
|
+
color: var(--text);
|
|
1954
|
+
}
|
|
1955
|
+
.run-stop-btn:disabled { opacity: 0.6; cursor: default; }
|
|
1956
|
+
|
|
1938
1957
|
/* Compile-notes strip over the canvas (CONCERNS §1). These are info-level FYIs
|
|
1939
1958
|
(e.g. AWARE's read-mode-on-exec note), so they read as info — not a warning —
|
|
1940
1959
|
and collapse to a faint one-line pill so they don't eat canvas height. */
|
package/dist/web/aware.js
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
const $reportOpen = document.getElementById('report-open');
|
|
25
25
|
const $reportShare = document.getElementById('report-share');
|
|
26
26
|
const $reportClose = document.getElementById('report-close');
|
|
27
|
+
const $stopRunBtn = document.getElementById('stop-run-btn');
|
|
27
28
|
|
|
28
29
|
// One persistent array the inspect Execution tab reads; we mutate in place so
|
|
29
30
|
// every AGENTS[node].execution reference stays valid across re-renders.
|
|
@@ -327,7 +328,10 @@
|
|
|
327
328
|
};
|
|
328
329
|
$runState.dataset.tip = stateTips[app.runState] || '';
|
|
329
330
|
|
|
330
|
-
|
|
331
|
+
// Keep Run disabled (and the header Stop shown) if a gate re-paint lands mid-run (#39) —
|
|
332
|
+
// never let a re-render re-arm Run while a run is still in flight.
|
|
333
|
+
$runBtn.disabled = !app.runnable || reportRunning || state.running;
|
|
334
|
+
if ($stopRunBtn) $stopRunBtn.hidden = !(reportRunning || state.running);
|
|
331
335
|
$runBtn.dataset.tip = app.runnable
|
|
332
336
|
? 'Run the approved workflow'
|
|
333
337
|
: app.runState === 'drift'
|
|
@@ -787,11 +791,22 @@
|
|
|
787
791
|
const saveItem = document.querySelector('.menu-item[data-action="save"]');
|
|
788
792
|
if (saveItem) saveItem.classList.toggle('menu-item-dirty', dirty);
|
|
789
793
|
}
|
|
794
|
+
// Set while WE programmatically reload to adopt a self-update (#37) — an in-place build
|
|
795
|
+
// adoption is not the user navigating away, so it must NOT trigger the unsaved-changes
|
|
796
|
+
// prompt below (that would strand the tab behind a modal). The reload path persists the
|
|
797
|
+
// inputs first, so nothing is lost.
|
|
798
|
+
let selfUpdating = false;
|
|
790
799
|
// Refresh/close guard: unsaved input values live only in memory, so warn before
|
|
791
800
|
// the page unloads while dirty (the browser shows its own generic confirm).
|
|
792
801
|
window.addEventListener('beforeunload', (e) => {
|
|
793
|
-
if (isInputsDirty(currentId)) { e.preventDefault(); e.returnValue = ''; }
|
|
802
|
+
if (!selfUpdating && isInputsDirty(currentId)) { e.preventDefault(); e.returnValue = ''; }
|
|
794
803
|
});
|
|
804
|
+
// Persist the active app's inputs to localStorage (the new build restores them on load).
|
|
805
|
+
// Used before a self-update reload so an in-place adoption never drops unsaved edits.
|
|
806
|
+
function persistCurrentInputs() {
|
|
807
|
+
if (!currentId) return;
|
|
808
|
+
try { localStorage.setItem(lsInputsKey(currentId), JSON.stringify(appInputValues.get(currentId) || {})); } catch { /* private mode / quota */ }
|
|
809
|
+
}
|
|
795
810
|
|
|
796
811
|
function seedAppInputs(app) {
|
|
797
812
|
if (app && Array.isArray(app.inputs) && app.inputs.length) {
|
|
@@ -896,6 +911,35 @@
|
|
|
896
911
|
// double-click LOAD the last result instantly instead of re-running.
|
|
897
912
|
const lastReportByApp = new Map();
|
|
898
913
|
|
|
914
|
+
// Reflect run-in-flight state in the ALWAYS-VISIBLE header so a run is stoppable even
|
|
915
|
+
// when the HTML Viewer modal is closed (#39). The header ■ Stop run appears for the whole
|
|
916
|
+
// duration of either run path (the modal run `reportRunning` and the inline run
|
|
917
|
+
// `state.running`) and disappears when neither is active. Run stays disabled while a run
|
|
918
|
+
// is in flight; when idle it falls back to the compile-gate's runnable state. Also tells
|
|
919
|
+
// the user, on the modal's × tooltip, that closing leaves the run going (Stop is in the
|
|
920
|
+
// header) — the canvas keeps showing progress (markCanvasRunning), by design.
|
|
921
|
+
function syncRunControls() {
|
|
922
|
+
const running = reportRunning || state.running;
|
|
923
|
+
if ($stopRunBtn) {
|
|
924
|
+
$stopRunBtn.hidden = !running;
|
|
925
|
+
// Reset to a fresh, clickable Stop each run; stopRun() flips it to "Cancelling…".
|
|
926
|
+
if (running && !cancelRequested) { $stopRunBtn.disabled = false; $stopRunBtn.textContent = '■ Stop run'; }
|
|
927
|
+
}
|
|
928
|
+
if (running) {
|
|
929
|
+
$runBtn.disabled = true;
|
|
930
|
+
} else {
|
|
931
|
+
const app = currentId && apps.get(currentId);
|
|
932
|
+
$runBtn.disabled = !(app && app.runnable);
|
|
933
|
+
}
|
|
934
|
+
// Rely on the native `disabled` attribute for AT state (paintGate also flips it); a
|
|
935
|
+
// separate aria-disabled would be redundant and could go stale on a gate re-paint.
|
|
936
|
+
if ($reportClose) {
|
|
937
|
+
$reportClose.dataset.tip = reportRunning
|
|
938
|
+
? 'Close viewer (the run keeps going — ■ Stop run is in the header)'
|
|
939
|
+
: 'Close';
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
899
943
|
// Stop the in-flight run (the escape hatch for a hung/unattached host). Marks
|
|
900
944
|
// cancelRequested so the catch shows "cancelled", clears the canvas running
|
|
901
945
|
// pulse, and asks the server to kill the `aware app run` child. The in-flight
|
|
@@ -904,8 +948,10 @@
|
|
|
904
948
|
if (!reportRunning && !state.running) return;
|
|
905
949
|
cancelRequested = true;
|
|
906
950
|
clearNodeStatus();
|
|
951
|
+
// Reflect "cancelling" on BOTH Stop surfaces — the in-modal overlay and the header.
|
|
907
952
|
const stopBtn = document.querySelector('.overlay-stop');
|
|
908
953
|
if (stopBtn) { stopBtn.disabled = true; stopBtn.textContent = 'Cancelling…'; }
|
|
954
|
+
if ($stopRunBtn) { $stopRunBtn.disabled = true; $stopRunBtn.textContent = 'Cancelling…'; }
|
|
909
955
|
try { await api('/api/run/stop', { method: 'POST' }); } catch { /* the run's own catch surfaces it */ }
|
|
910
956
|
}
|
|
911
957
|
|
|
@@ -946,6 +992,7 @@
|
|
|
946
992
|
reportRunning = true;
|
|
947
993
|
cancelRequested = false;
|
|
948
994
|
markCanvasRunning(); // paints behind the modal; visible once it's closed
|
|
995
|
+
syncRunControls(); // header ■ Stop run + Run disabled — reachable even if the modal is closed (#39)
|
|
949
996
|
|
|
950
997
|
const inputs = currentInputs();
|
|
951
998
|
const inputBadge = Object.entries(inputs).map(([k, v]) => `${k}=${v}`).join(' · ');
|
|
@@ -1004,6 +1051,7 @@
|
|
|
1004
1051
|
}
|
|
1005
1052
|
} finally {
|
|
1006
1053
|
reportRunning = false;
|
|
1054
|
+
syncRunControls(); // run ended — hide the header Stop, restore Run from the gate
|
|
1007
1055
|
}
|
|
1008
1056
|
}
|
|
1009
1057
|
|
|
@@ -1264,6 +1312,8 @@
|
|
|
1264
1312
|
// The Stop button is rebuilt into the overlay each run — delegate so one
|
|
1265
1313
|
// listener survives every innerHTML swap.
|
|
1266
1314
|
$reportOverlay.addEventListener('click', (e) => { if (e.target.closest('.overlay-stop')) stopRun(); });
|
|
1315
|
+
// The header ■ Stop run — the always-reachable twin of the overlay Stop (#39).
|
|
1316
|
+
if ($stopRunBtn) $stopRunBtn.onclick = () => stopRun();
|
|
1267
1317
|
$reportOpen.onclick = () => {
|
|
1268
1318
|
const html = $reportFrame.srcdoc;
|
|
1269
1319
|
if (!html) return;
|
|
@@ -2064,6 +2114,7 @@
|
|
|
2064
2114
|
$runBtn.disabled = true;
|
|
2065
2115
|
if ($simBtn) $simBtn.disabled = true;
|
|
2066
2116
|
$runBtn.textContent = '◆ Running…';
|
|
2117
|
+
syncRunControls(); // surface the header ■ Stop run for the inline (no-modal) run too (#39)
|
|
2067
2118
|
liveTrace.length = 0;
|
|
2068
2119
|
state.hasRun = true;
|
|
2069
2120
|
try {
|
|
@@ -2102,6 +2153,7 @@
|
|
|
2102
2153
|
$runBtn.disabled = false;
|
|
2103
2154
|
if ($simBtn) $simBtn.disabled = false;
|
|
2104
2155
|
$runBtn.textContent = '▶ Run workflow';
|
|
2156
|
+
syncRunControls(); // hide the header Stop; reconcile Run with the gate's runnable state
|
|
2105
2157
|
}
|
|
2106
2158
|
}
|
|
2107
2159
|
$runBtn.onclick = () => runApp({ simulate: false });
|
|
@@ -2572,6 +2624,7 @@
|
|
|
2572
2624
|
}
|
|
2573
2625
|
}
|
|
2574
2626
|
let shownVersion = false;
|
|
2627
|
+
let loadedAppVersion = null; // the build this tab is running against (first health wins)
|
|
2575
2628
|
function startHealthPoll() {
|
|
2576
2629
|
const tick = async () => {
|
|
2577
2630
|
let ok = false;
|
|
@@ -2592,7 +2645,25 @@
|
|
|
2592
2645
|
// (popover + what's-new). Empty until /api/health reports it → links omitted.
|
|
2593
2646
|
if (h && h.webBase) webBase = h.webBase;
|
|
2594
2647
|
const av = document.getElementById('app-version');
|
|
2595
|
-
if (av && h && h.appVersion && !shownVersion) { av.textContent = '
|
|
2648
|
+
if (av && h && h.appVersion && !shownVersion) { av.textContent = 'FloLess ' + h.appVersion; shownVersion = true; }
|
|
2649
|
+
// Adopt a self-update IN PLACE (#37): this tab loaded build X, but the server now
|
|
2650
|
+
// reports build Y — a self-update swapped the build under us. Reload so the tab runs
|
|
2651
|
+
// the NEW build's web assets (and a fresh footer + what's-new) instead of being
|
|
2652
|
+
// stranded on the old build's UI — stuck "Updating…", a frozen version, version skew.
|
|
2653
|
+
// This is what makes the post-update tab genuinely "reusable": the existing tab
|
|
2654
|
+
// becomes the new-version tab. Only fires on a real version change (first health sets
|
|
2655
|
+
// the baseline), so normal restarts / same-version polls never reload.
|
|
2656
|
+
if (h && h.appVersion) {
|
|
2657
|
+
if (loadedAppVersion === null) loadedAppVersion = h.appVersion;
|
|
2658
|
+
else if (h.appVersion !== loadedAppVersion) {
|
|
2659
|
+
// Persist unsaved inputs and suppress the beforeunload prompt (this is an
|
|
2660
|
+
// in-place build adoption, not the user leaving) before reloading.
|
|
2661
|
+
selfUpdating = true;
|
|
2662
|
+
persistCurrentInputs();
|
|
2663
|
+
location.reload();
|
|
2664
|
+
return;
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2596
2667
|
// After the build version is stamped, reveal the relaunch-surviving what's-new
|
|
2597
2668
|
// panel iff this is the build we just self-updated into (guarded to once).
|
|
2598
2669
|
maybeShowWhatsNew();
|
|
@@ -3716,7 +3787,7 @@
|
|
|
3716
3787
|
riAwareVersion = h.awareVersion || '';
|
|
3717
3788
|
const app = currentId && apps.get(currentId);
|
|
3718
3789
|
const wf = app ? ` · workflow "${app.displayName || currentId}"` : '';
|
|
3719
|
-
$riContext.textContent = `Also sent automatically: FloLess
|
|
3790
|
+
$riContext.textContent = `Also sent automatically: FloLess ${h.appVersion || '?'}, AWARE ${h.awareVersion || '?'}${wf}`;
|
|
3720
3791
|
}).catch(() => { /* keep the generic line on a health blip */ });
|
|
3721
3792
|
showModal($riModal);
|
|
3722
3793
|
setTimeout(() => $riTitle.focus(), 0);
|
|
@@ -3971,7 +4042,7 @@
|
|
|
3971
4042
|
// /api/health is un-gated, so this works even while unlicensed.
|
|
3972
4043
|
fetch('/api/health', { cache: 'no-store' })
|
|
3973
4044
|
.then((r) => r.json())
|
|
3974
|
-
.then((h) => { if (h && h.appVersion) document.getElementById('lg-version').textContent = '
|
|
4045
|
+
.then((h) => { if (h && h.appVersion) document.getElementById('lg-version').textContent = 'FloLess ' + h.appVersion; })
|
|
3975
4046
|
.catch(() => { /* version is a nicety; never block the gate on it */ });
|
|
3976
4047
|
document.getElementById('lg-signin').onclick = async () => {
|
|
3977
4048
|
const s = document.getElementById('lg-status');
|
package/dist/web/index.html
CHANGED
|
@@ -60,10 +60,11 @@
|
|
|
60
60
|
<button id="browse-btn" data-tip="Browse all installed agents">⊞ Agents</button>
|
|
61
61
|
<button id="routines-btn" data-tip="Routines — run a workflow on a schedule or a live trigger">⏱ Routines</button>
|
|
62
62
|
<span class="ctl-sep" aria-hidden="true"></span>
|
|
63
|
-
<span class="run-state" id="run-state"></span>
|
|
63
|
+
<span class="run-state" id="run-state" role="status" aria-live="polite"></span>
|
|
64
64
|
<button id="compile-btn" data-tip="Compile + approve → freeze the .lock">⎙ Compile</button>
|
|
65
65
|
<button id="sim-btn" data-tip="Simulate: stub every node from its output schema — no live host is contacted. Validates the workflow's composition end-to-end, even when the real agents aren't connected yet.">Simulate</button>
|
|
66
66
|
<button id="run-btn" data-tip="Run the workflow for real against the live host (uses the inputs; renders the report node in the HTML Viewer)">▶ Run workflow</button>
|
|
67
|
+
<button id="stop-run-btn" class="run-stop-btn" type="button" aria-label="Stop the current run" data-tip="Stop the in-flight run (always reachable, even with the HTML Viewer closed)" hidden>■ Stop run</button>
|
|
67
68
|
</div>
|
|
68
69
|
</header>
|
|
69
70
|
|
package/launch.mjs
CHANGED
|
@@ -17,6 +17,7 @@ import { createInterface } from 'node:readline';
|
|
|
17
17
|
import { rotateLog, openLogFd } from './log.mjs';
|
|
18
18
|
import { supervisorPidsToKill, teardownDecision, awareIsPresent, RUN_KEY, RUN_VALUE, PROTOCOL_KEY } from './teardown.mjs';
|
|
19
19
|
import { resolveRealInstallExe } from './install-path.mjs';
|
|
20
|
+
import { consumePostUpdateMarker } from './post-update-marker.mjs';
|
|
20
21
|
|
|
21
22
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
22
23
|
const PORT = Number(process.env.PORT ?? 4317);
|
|
@@ -163,6 +164,11 @@ async function ensureServerUp() {
|
|
|
163
164
|
}
|
|
164
165
|
|
|
165
166
|
export async function cmdOpen() {
|
|
167
|
+
// A post-self-update relaunch must start the server but NOT open a second browser tab —
|
|
168
|
+
// Velopack relaunches FlolessApp.exe bare (→ this `open` action), while the user's existing
|
|
169
|
+
// tab is still alive and reconnects via its 5s health poll (#37). Consume the one-shot
|
|
170
|
+
// marker the updater dropped before the Update.exe handoff; if present, behave like `start`.
|
|
171
|
+
const postUpdate = consumePostUpdateMarker();
|
|
166
172
|
if (await ping()) {
|
|
167
173
|
// Something is already serving the port. If it's a DIFFERENT, OLDER floless.app build than
|
|
168
174
|
// this one, take it over ("newest wins") so the version the user just launched is the one
|
|
@@ -174,11 +180,15 @@ export async function cmdOpen() {
|
|
|
174
180
|
await new Promise((r) => setTimeout(r, 500)); // let the OS release the port
|
|
175
181
|
await ensureServerUp();
|
|
176
182
|
} else {
|
|
177
|
-
log(`already running${running ? ` (v${running})` : ''}
|
|
183
|
+
log(`already running${running ? ` (v${running})` : ''}`);
|
|
178
184
|
}
|
|
179
185
|
} else {
|
|
180
186
|
await ensureServerUp();
|
|
181
187
|
}
|
|
188
|
+
if (postUpdate) {
|
|
189
|
+
log('post-update relaunch — server up; the existing tab reconnects via its health poll (no new tab)');
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
182
192
|
log(`opening ${BROWSER_URL}`);
|
|
183
193
|
openBrowser(BROWSER_URL);
|
|
184
194
|
}
|