@floless/app 0.56.0 → 0.57.0

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.
@@ -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: Worker2 } = require("worker_threads");
5492
- var { join: join29 } = require("path");
5492
+ var { join: join30 } = 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"] || join29(__dirname, "lib", "worker.js");
5543
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join30(__dirname, "lib", "worker.js");
5544
5544
  const worker = new Worker2(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: createRequire5 } = require("module");
6009
- var { existsSync: existsSync24 } = require("node:fs");
6009
+ var { existsSync: existsSync25 } = require("node:fs");
6010
6010
  var getCallers = require_caller();
6011
- var { join: join29, isAbsolute: isAbsolute2, sep: sep4 } = require("node:path");
6011
+ var { join: join30, isAbsolute: isAbsolute2, sep: sep4 } = require("node:path");
6012
6012
  var { fileURLToPath: fileURLToPath6 } = 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) && !existsSync24(path);
6083
+ return isAbsolute2(path) && !existsSync25(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"] || join29(__dirname, "worker.js");
6164
+ target = bundlerOverrides["pino-worker"] || join30(__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"] || join29(__dirname, "worker.js");
6182
+ target = bundlerOverrides["pino-worker"] || join30(__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 join29(__dirname, "..", "file.js");
6205
+ return join30(__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 join29 = ",";
7185
+ let join30 = ",";
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
- join29 = `,
7199
+ join30 = `,
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 += join29;
7207
+ res += join30;
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 += `${join29}"... ${getItemCount(removedKeys)} not stringified"`;
7213
+ res += `${join30}"... ${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
- join29 = `,
7234
+ join30 = `,
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 = join29;
7248
+ separator = join30;
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 = join29;
7254
+ separator = join30;
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 join29 = ",";
7295
+ let join30 = ",";
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
- join29 = `,
7308
+ join30 = `,
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 += join29;
7316
+ res += join30;
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 += `${join29}"... ${getItemCount(removedKeys)} not stringified"`;
7322
+ res += `${join30}"... ${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
- join29 = `,
7335
+ join30 = `,
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 = join29;
7344
+ separator = join30;
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 join30 = `,
7402
+ const join31 = `,
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 += join30;
7409
+ res2 += join31;
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 += `${join30}"... ${getItemCount(removedKeys)} not stringified"`;
7415
+ res2 += `${join31}"... ${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 join29 = `,
7431
+ const join30 = `,
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, join29, maximumBreadth);
7437
+ res += stringifyTypedArray(value, join30, maximumBreadth);
7438
7438
  keys = keys.slice(value.length);
7439
7439
  maximumPropertiesToStringify -= value.length;
7440
- separator = join29;
7440
+ separator = join30;
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 = join29;
7451
+ separator = join30;
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 = join29;
7457
+ separator = join30;
7458
7458
  }
7459
7459
  if (separator !== "") {
7460
7460
  res = `
@@ -25041,8 +25041,8 @@ var require_patch = __commonJS({
25041
25041
  "node_modules/semver/functions/patch.js"(exports2, module2) {
25042
25042
  "use strict";
25043
25043
  var SemVer = require_semver();
25044
- var patch = (a, loose) => new SemVer(a, loose).patch;
25045
- module2.exports = patch;
25044
+ var patch2 = (a, loose) => new SemVer(a, loose).patch;
25045
+ module2.exports = patch2;
25046
25046
  }
25047
25047
  });
25048
25048
 
@@ -25270,10 +25270,10 @@ var require_coerce = __commonJS({
25270
25270
  }
25271
25271
  const major = match[2];
25272
25272
  const minor = match[3] || "0";
25273
- const patch = match[4] || "0";
25273
+ const patch2 = match[4] || "0";
25274
25274
  const prerelease = options.includePrerelease && match[5] ? `-${match[5]}` : "";
25275
25275
  const build = options.includePrerelease && match[6] ? `+${match[6]}` : "";
25276
- return parse(`${major}.${minor}.${patch}${prerelease}${build}`, options);
25276
+ return parse(`${major}.${minor}.${patch2}${prerelease}${build}`, options);
25277
25277
  };
25278
25278
  module2.exports = coerce;
25279
25279
  }
@@ -26338,7 +26338,7 @@ var require_semver2 = __commonJS({
26338
26338
  var diff = require_diff();
26339
26339
  var major = require_major();
26340
26340
  var minor = require_minor();
26341
- var patch = require_patch();
26341
+ var patch2 = require_patch();
26342
26342
  var prerelease = require_prerelease();
26343
26343
  var compare = require_compare();
26344
26344
  var rcompare = require_rcompare();
@@ -26377,7 +26377,7 @@ var require_semver2 = __commonJS({
26377
26377
  diff,
26378
26378
  major,
26379
26379
  minor,
26380
- patch,
26380
+ patch: patch2,
26381
26381
  prerelease,
26382
26382
  compare,
26383
26383
  rcompare,
@@ -28698,13 +28698,13 @@ var require_accept_version = __commonJS({
28698
28698
  if (typeof version !== "string") {
28699
28699
  throw new TypeError("Version should be a string");
28700
28700
  }
28701
- let [major, minor, patch] = version.split(".", 3);
28701
+ let [major, minor, patch2] = version.split(".", 3);
28702
28702
  if (isNaN(major)) {
28703
28703
  throw new TypeError("Major version must be a numeric value");
28704
28704
  }
28705
28705
  major = Number(major);
28706
28706
  minor = Number(minor) || 0;
28707
- patch = Number(patch) || 0;
28707
+ patch2 = Number(patch2) || 0;
28708
28708
  if (major >= this.maxMajor) {
28709
28709
  this.maxMajor = major;
28710
28710
  this.store.set("x", store);
@@ -28717,11 +28717,11 @@ var require_accept_version = __commonJS({
28717
28717
  this.store.set(`${major}.x`, store);
28718
28718
  this.store.set(`${major}.x.x`, store);
28719
28719
  }
28720
- if (patch >= (this.maxPatches[`${major}.${minor}`] || 0)) {
28721
- this.maxPatches[`${major}.${minor}`] = patch;
28720
+ if (patch2 >= (this.maxPatches[`${major}.${minor}`] || 0)) {
28721
+ this.maxPatches[`${major}.${minor}`] = patch2;
28722
28722
  this.store.set(`${major}.${minor}.x`, store);
28723
28723
  }
28724
- this.store.set(`${major}.${minor}.${patch}`, store);
28724
+ this.store.set(`${major}.${minor}.${patch2}`, store);
28725
28725
  return this;
28726
28726
  };
28727
28727
  SemVerStore.prototype.get = function(version) {
@@ -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 join29 = path.join;
41994
+ var join30 = path.join;
41995
41995
  var normalize2 = path.normalize;
41996
41996
  var resolve6 = path.resolve;
41997
41997
  var sep4 = path.sep;
@@ -42078,7 +42078,7 @@ var require_send = __commonJS({
42078
42078
  return { statusCode: 403 };
42079
42079
  }
42080
42080
  parts = path2.split(sep4);
42081
- path2 = normalize2(join29(root, path2));
42081
+ path2 = normalize2(join30(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 err2;
42362
42362
  for (let i = 0; i < options.index.length; i++) {
42363
42363
  const index = options.index[i];
42364
- const p = join29(path2, index);
42364
+ const p = join30(path2, index);
42365
42365
  const { error, stat: stat4 } = await tryStat(p);
42366
42366
  if (error) {
42367
42367
  err2 = error;
@@ -43145,8 +43145,8 @@ var require_static = __commonJS({
43145
43145
  }
43146
43146
  }
43147
43147
  }
43148
- for (const [dirname12, rootPath] of indexDirs.entries()) {
43149
- const pathname = dirname12 + (dirname12.endsWith("/") ? "" : "/");
43148
+ for (const [dirname13, rootPath] of indexDirs.entries()) {
43149
+ const pathname = dirname13 + (dirname13.endsWith("/") ? "" : "/");
43150
43150
  const file = "/" + pathname.replace(prefix, "");
43151
43151
  setUpHeadAndGet(routeOpts, pathname, file, rootPath);
43152
43152
  if (opts.redirect === true) {
@@ -50649,7 +50649,7 @@ var require_public_api = __commonJS({
50649
50649
  return docs;
50650
50650
  return Object.assign([], { empty: true }, composer$1.streamInfo());
50651
50651
  }
50652
- function parseDocument4(source, options = {}) {
50652
+ function parseDocument5(source, options = {}) {
50653
50653
  const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options);
50654
50654
  const parser$1 = new parser.Parser(lineCounter2?.addNewLine);
50655
50655
  const composer$1 = new composer.Composer(options);
@@ -50675,7 +50675,7 @@ var require_public_api = __commonJS({
50675
50675
  } else if (options === void 0 && reviver && typeof reviver === "object") {
50676
50676
  options = reviver;
50677
50677
  }
50678
- const doc = parseDocument4(src, options);
50678
+ const doc = parseDocument5(src, options);
50679
50679
  if (!doc)
50680
50680
  return null;
50681
50681
  doc.warnings.forEach((warning) => log2.warn(doc.options.logLevel, warning));
@@ -50711,7 +50711,7 @@ var require_public_api = __commonJS({
50711
50711
  }
50712
50712
  exports2.parse = parse;
50713
50713
  exports2.parseAllDocuments = parseAllDocuments;
50714
- exports2.parseDocument = parseDocument4;
50714
+ exports2.parseDocument = parseDocument5;
50715
50715
  exports2.stringify = stringify;
50716
50716
  }
50717
50717
  });
@@ -50827,9 +50827,9 @@ var import_node_readline2 = require("node:readline");
50827
50827
 
50828
50828
  // index.ts
50829
50829
  var import_node_url5 = require("node:url");
50830
- var import_node_path27 = require("node:path");
50831
- var import_node_os19 = require("node:os");
50832
- var import_node_fs29 = require("node:fs");
50830
+ var import_node_path28 = require("node:path");
50831
+ var import_node_os20 = require("node:os");
50832
+ var import_node_fs30 = require("node:fs");
50833
50833
  var import_node_child_process7 = require("node:child_process");
50834
50834
 
50835
50835
  // log.mjs
@@ -50875,19 +50875,180 @@ var import_static = __toESM(require_static(), 1);
50875
50875
 
50876
50876
  // aware-adapter.ts
50877
50877
  var import_node_child_process = require("node:child_process");
50878
- var import_node_fs4 = require("node:fs");
50879
- var import_node_path3 = require("node:path");
50880
- var import_node_os3 = require("node:os");
50881
- var import_node_crypto2 = require("node:crypto");
50878
+ var import_node_fs6 = require("node:fs");
50879
+ var import_node_path5 = require("node:path");
50880
+ var import_node_os5 = require("node:os");
50881
+ var import_node_crypto3 = require("node:crypto");
50882
50882
  var import_node_module = require("node:module");
50883
50883
 
50884
50884
  // app-reader.ts
50885
+ var import_node_crypto2 = require("node:crypto");
50886
+ var import_node_fs5 = require("node:fs");
50887
+ var import_node_os4 = require("node:os");
50888
+ var import_node_path4 = require("node:path");
50889
+ var import_yaml = __toESM(require_dist6(), 1);
50890
+
50891
+ // app-origin-store.ts
50892
+ var import_node_fs4 = require("node:fs");
50893
+ var import_node_os3 = require("node:os");
50894
+ var import_node_path3 = require("node:path");
50895
+
50896
+ // visual-input-store.ts
50885
50897
  var import_node_crypto = require("node:crypto");
50886
50898
  var import_node_fs3 = require("node:fs");
50887
50899
  var import_node_os2 = require("node:os");
50888
50900
  var import_node_path2 = require("node:path");
50889
- var import_yaml = __toESM(require_dist6(), 1);
50890
- var APPS_DIR = process.env.AWARE_HOME ? (0, import_node_path2.join)(process.env.AWARE_HOME, "apps") : (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".aware", "apps");
50901
+ var MAX_BYTES2 = 25 * 1024 * 1024;
50902
+ var VisualInputError = class extends Error {
50903
+ };
50904
+ function inputsDir() {
50905
+ const root = process.env.FLOLESS_HOME ?? (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".floless");
50906
+ return (0, import_node_path2.join)(root, "inputs");
50907
+ }
50908
+ function sniffExt(buf) {
50909
+ if (buf.length >= 8 && buf[0] === 137 && buf[1] === 80 && buf[2] === 78 && buf[3] === 71) return "png";
50910
+ if (buf.length >= 3 && buf[0] === 255 && buf[1] === 216 && buf[2] === 255) return "jpg";
50911
+ if (buf.length >= 12 && buf.toString("ascii", 0, 4) === "RIFF" && buf.toString("ascii", 8, 12) === "WEBP") return "webp";
50912
+ if (buf.length >= 5 && buf.toString("ascii", 0, 5) === "%PDF-") return "pdf";
50913
+ return null;
50914
+ }
50915
+ var RESERVED = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\..*)?$/i;
50916
+ function safeSegment(s) {
50917
+ if (!s || s === "." || s.includes("..")) return false;
50918
+ if (s.includes("/") || s.includes("\\") || s.includes(import_node_path2.sep)) return false;
50919
+ for (let i = 0; i < s.length; i++) {
50920
+ const c = s.charCodeAt(i);
50921
+ if (c < 32 || c === 58) return false;
50922
+ }
50923
+ if (/[. ]$/.test(s)) return false;
50924
+ return !RESERVED.test(s);
50925
+ }
50926
+ function storeVisualInput(appId, dataUrl) {
50927
+ if (!safeSegment(appId)) throw new VisualInputError("invalid app id");
50928
+ const m = /^data:([\w/+.-]+);base64,(.*)$/s.exec(dataUrl || "");
50929
+ if (!m) throw new VisualInputError("visual input must be a base64 data URL");
50930
+ const buf = Buffer.from(m[2] ?? "", "base64");
50931
+ if (buf.length === 0) throw new VisualInputError("empty visual input");
50932
+ if (buf.length > MAX_BYTES2) throw new VisualInputError("visual input too large (max 25 MB)");
50933
+ const ext = sniffExt(buf);
50934
+ if (!ext) throw new VisualInputError("visual input must be PNG, JPEG, WebP, or PDF");
50935
+ const sha2562 = (0, import_node_crypto.createHash)("sha256").update(buf).digest("hex");
50936
+ const dir = (0, import_node_path2.join)(inputsDir(), appId);
50937
+ const path = (0, import_node_path2.join)(dir, `${sha2562}.${ext}`);
50938
+ const root = (0, import_node_path2.resolve)(inputsDir());
50939
+ const target = (0, import_node_path2.resolve)(path);
50940
+ if (target !== root && !target.startsWith(root + import_node_path2.sep)) {
50941
+ throw new VisualInputError("refusing to write outside the inputs root");
50942
+ }
50943
+ (0, import_node_fs3.mkdirSync)(dir, { recursive: true });
50944
+ if (!(0, import_node_fs3.existsSync)(path)) {
50945
+ const tmp = `${path}.tmp`;
50946
+ (0, import_node_fs3.writeFileSync)(tmp, buf);
50947
+ (0, import_node_fs3.renameSync)(tmp, path);
50948
+ }
50949
+ return { path, ext, bytes: (0, import_node_fs3.statSync)(path).size, sha256: sha2562 };
50950
+ }
50951
+ function renameVisualInputs(oldId, newId) {
50952
+ if (!safeSegment(oldId) || !safeSegment(newId)) throw new VisualInputError("invalid app id");
50953
+ const from = (0, import_node_path2.join)(inputsDir(), oldId);
50954
+ if (!(0, import_node_fs3.existsSync)(from)) return;
50955
+ const to = (0, import_node_path2.join)(inputsDir(), newId);
50956
+ if ((0, import_node_fs3.existsSync)(to)) (0, import_node_fs3.rmSync)(to, { recursive: true, force: true });
50957
+ (0, import_node_fs3.mkdirSync)(inputsDir(), { recursive: true });
50958
+ (0, import_node_fs3.renameSync)(from, to);
50959
+ }
50960
+ function copyVisualInputs(srcId, newId) {
50961
+ if (!safeSegment(srcId) || !safeSegment(newId)) throw new VisualInputError("invalid app id");
50962
+ const from = (0, import_node_path2.join)(inputsDir(), srcId);
50963
+ if (!(0, import_node_fs3.existsSync)(from)) return;
50964
+ (0, import_node_fs3.cpSync)(from, (0, import_node_path2.join)(inputsDir(), newId), { recursive: true });
50965
+ }
50966
+ function deleteVisualInputs(id) {
50967
+ if (!safeSegment(id)) throw new VisualInputError("invalid app id");
50968
+ const dir = (0, import_node_path2.join)(inputsDir(), id);
50969
+ if ((0, import_node_fs3.existsSync)(dir)) (0, import_node_fs3.rmSync)(dir, { recursive: true, force: true });
50970
+ }
50971
+
50972
+ // app-origin-store.ts
50973
+ function storeFile() {
50974
+ const root = process.env.FLOLESS_HOME ?? (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".floless");
50975
+ return (0, import_node_path3.join)(root, "app-origin.json");
50976
+ }
50977
+ function loadAll() {
50978
+ try {
50979
+ const parsed = JSON.parse((0, import_node_fs4.readFileSync)(storeFile(), "utf8"));
50980
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
50981
+ } catch {
50982
+ return {};
50983
+ }
50984
+ }
50985
+ function saveAll(map) {
50986
+ const file = storeFile();
50987
+ const dir = (0, import_node_path3.dirname)(file);
50988
+ if (!(0, import_node_fs4.existsSync)(dir)) (0, import_node_fs4.mkdirSync)(dir, { recursive: true });
50989
+ const tmp = `${file}.${process.pid}.tmp`;
50990
+ (0, import_node_fs4.writeFileSync)(tmp, JSON.stringify(map, null, 2));
50991
+ (0, import_node_fs4.renameSync)(tmp, file);
50992
+ }
50993
+ function readOrigin(id) {
50994
+ if (!safeSegment(id)) return null;
50995
+ return loadAll()[id] ?? null;
50996
+ }
50997
+ function patch(id, fields) {
50998
+ if (!safeSegment(id)) return;
50999
+ const all = loadAll();
51000
+ all[id] = { ...all[id] ?? {}, ...fields };
51001
+ saveAll(all);
51002
+ }
51003
+ function setDerivedFrom(id, derivedFrom) {
51004
+ patch(id, { derivedFrom });
51005
+ }
51006
+ function resolveTemplateId(id) {
51007
+ return readOrigin(id)?.derivedFrom ?? id;
51008
+ }
51009
+ function markForked(id, who, ts) {
51010
+ patch(id, { forkedAt: ts, forkedFromVersion: who.version, liabilityAcceptedAt: ts, liabilityAcceptedBy: who.acceptedBy });
51011
+ }
51012
+ function clearFork(id) {
51013
+ if (!safeSegment(id)) return;
51014
+ const all = loadAll();
51015
+ const prev = all[id];
51016
+ if (!prev) return;
51017
+ const next = {};
51018
+ if (prev.derivedFrom) next.derivedFrom = prev.derivedFrom;
51019
+ all[id] = next;
51020
+ saveAll(all);
51021
+ }
51022
+ function renameOrigin(oldId, newId) {
51023
+ if (!safeSegment(oldId) || !safeSegment(newId)) return;
51024
+ const all = loadAll();
51025
+ const prev = all[oldId] ?? { derivedFrom: oldId };
51026
+ delete all[oldId];
51027
+ all[newId] = prev;
51028
+ saveAll(all);
51029
+ }
51030
+ function copyOrigin(srcId, newId) {
51031
+ setDerivedFrom(newId, resolveTemplateId(srcId));
51032
+ }
51033
+ function deleteOrigin(id) {
51034
+ if (!safeSegment(id)) return;
51035
+ const all = loadAll();
51036
+ if (id in all) {
51037
+ delete all[id];
51038
+ saveAll(all);
51039
+ }
51040
+ }
51041
+ function appOrigin(id) {
51042
+ try {
51043
+ const rec = readOrigin(id);
51044
+ return { templateId: rec?.derivedFrom ?? id, forked: !!rec?.forkedAt, derivedFrom: rec?.derivedFrom ?? null };
51045
+ } catch {
51046
+ return { templateId: id, forked: false, derivedFrom: null };
51047
+ }
51048
+ }
51049
+
51050
+ // app-reader.ts
51051
+ var APPS_DIR = process.env.AWARE_HOME ? (0, import_node_path4.join)(process.env.AWARE_HOME, "apps") : (0, import_node_path4.join)((0, import_node_os4.homedir)(), ".aware", "apps");
50891
51052
  function normalizeNote(n) {
50892
51053
  if (n && typeof n === "object") {
50893
51054
  const o = n;
@@ -50916,24 +51077,24 @@ var AppNotFoundError = class extends Error {
50916
51077
  };
50917
51078
  function appDir(id) {
50918
51079
  if (id.includes("/") || id.includes("\\") || id.includes("..")) throw new AppNotFoundError(id);
50919
- const dir = (0, import_node_path2.join)(APPS_DIR, id);
50920
- if (!(0, import_node_fs3.existsSync)(dir)) throw new AppNotFoundError(id);
51080
+ const dir = (0, import_node_path4.join)(APPS_DIR, id);
51081
+ if (!(0, import_node_fs5.existsSync)(dir)) throw new AppNotFoundError(id);
50921
51082
  return dir;
50922
51083
  }
50923
51084
  function appPath(id) {
50924
51085
  if (id.includes("/") || id.includes("\\") || id.includes("..")) throw new AppNotFoundError(id);
50925
- return (0, import_node_path2.join)(APPS_DIR, id);
51086
+ return (0, import_node_path4.join)(APPS_DIR, id);
50926
51087
  }
50927
51088
  function appExists(id) {
50928
51089
  if (id.includes("/") || id.includes("\\") || id.includes("..")) return false;
50929
- return (0, import_node_fs3.existsSync)((0, import_node_path2.join)(APPS_DIR, id));
51090
+ return (0, import_node_fs5.existsSync)((0, import_node_path4.join)(APPS_DIR, id));
50930
51091
  }
50931
51092
  function firstWithExt(dir, ext) {
50932
- const hit = (0, import_node_fs3.readdirSync)(dir).find((f) => f.toLowerCase().endsWith(ext));
50933
- return hit ? (0, import_node_path2.join)(dir, hit) : null;
51093
+ const hit = (0, import_node_fs5.readdirSync)(dir).find((f) => f.toLowerCase().endsWith(ext));
51094
+ return hit ? (0, import_node_path4.join)(dir, hit) : null;
50934
51095
  }
50935
51096
  function sha256(text) {
50936
- return (0, import_node_crypto.createHash)("sha256").update(text).digest("hex");
51097
+ return (0, import_node_crypto2.createHash)("sha256").update(text).digest("hex");
50937
51098
  }
50938
51099
  function asRecord(v) {
50939
51100
  return v && typeof v === "object" && !Array.isArray(v) ? v : {};
@@ -50943,7 +51104,7 @@ function appProvider(id) {
50943
51104
  const dir = appDir(id);
50944
51105
  const sourcePath = firstWithExt(dir, ".app") ?? firstWithExt(dir, ".flo");
50945
51106
  if (!sourcePath) return "other";
50946
- const src = asRecord((0, import_yaml.parse)((0, import_node_fs3.readFileSync)(sourcePath, "utf8")));
51107
+ const src = asRecord((0, import_yaml.parse)((0, import_node_fs5.readFileSync)(sourcePath, "utf8")));
50947
51108
  const srcNodes = Array.isArray(src.nodes) ? src.nodes : [];
50948
51109
  const counts = /* @__PURE__ */ new Map();
50949
51110
  for (const n of srcNodes) {
@@ -50967,7 +51128,7 @@ function appBaked(id) {
50967
51128
  const dir = appDir(id);
50968
51129
  const sourcePath = firstWithExt(dir, ".app") ?? firstWithExt(dir, ".flo");
50969
51130
  if (!sourcePath) return false;
50970
- const src = asRecord((0, import_yaml.parse)((0, import_node_fs3.readFileSync)(sourcePath, "utf8")));
51131
+ const src = asRecord((0, import_yaml.parse)((0, import_node_fs5.readFileSync)(sourcePath, "utf8")));
50971
51132
  return src["exposes-as-agent"] === true;
50972
51133
  } catch {
50973
51134
  return false;
@@ -50977,11 +51138,12 @@ function readApp(id) {
50977
51138
  const dir = appDir(id);
50978
51139
  const sourcePath = firstWithExt(dir, ".app") ?? firstWithExt(dir, ".flo");
50979
51140
  if (!sourcePath) throw new AppNotFoundError(id);
50980
- const sourceText = (0, import_node_fs3.readFileSync)(sourcePath, "utf8");
51141
+ const sourceText = (0, import_node_fs5.readFileSync)(sourcePath, "utf8");
50981
51142
  const sourceHash = sha256(sourceText);
50982
51143
  const src = asRecord((0, import_yaml.parse)(sourceText));
51144
+ const origin = appOrigin(id);
50983
51145
  const lockPath = firstWithExt(dir, ".lock");
50984
- const lockText = lockPath ? (0, import_node_fs3.readFileSync)(lockPath, "utf8") : null;
51146
+ const lockText = lockPath ? (0, import_node_fs5.readFileSync)(lockPath, "utf8") : null;
50985
51147
  const lockDoc = lockText ? asRecord((0, import_yaml.parse)(lockText)) : {};
50986
51148
  const lockNodes = Array.isArray(lockDoc.nodes) ? lockDoc.nodes : [];
50987
51149
  const lockById = new Map(lockNodes.map((n) => [String(n.id), n]));
@@ -51059,6 +51221,10 @@ function readApp(id) {
51059
51221
  displayName: typeof src["display-name"] === "string" ? src["display-name"] : id,
51060
51222
  description: typeof src.description === "string" ? src.description : "",
51061
51223
  version: src.version != null ? String(src.version) : "0.0.0",
51224
+ publisher: typeof src.publisher === "string" ? src.publisher : null,
51225
+ derivedFrom: origin.derivedFrom,
51226
+ templateId: origin.templateId,
51227
+ forked: origin.forked,
51062
51228
  layout,
51063
51229
  requires: Array.isArray(src.requires) ? src.requires.map(String) : [],
51064
51230
  inputs,
@@ -51082,11 +51248,11 @@ function sourceNodeId(nodes, connections) {
51082
51248
  function readCommandSpec(agent, command, agentsDir = AGENTS_DIR) {
51083
51249
  const safe = (n) => !/[/\\\0]/.test(n) && !n.includes("..");
51084
51250
  if (!safe(agent) || !safe(command)) return null;
51085
- const manifestPath = (0, import_node_path2.join)(agentsDir, agent, "manifest.yaml");
51086
- if (!(0, import_node_fs3.existsSync)(manifestPath)) return null;
51251
+ const manifestPath = (0, import_node_path4.join)(agentsDir, agent, "manifest.yaml");
51252
+ if (!(0, import_node_fs5.existsSync)(manifestPath)) return null;
51087
51253
  let doc;
51088
51254
  try {
51089
- doc = asRecord((0, import_yaml.parse)((0, import_node_fs3.readFileSync)(manifestPath, "utf8")));
51255
+ doc = asRecord((0, import_yaml.parse)((0, import_node_fs5.readFileSync)(manifestPath, "utf8")));
51090
51256
  } catch {
51091
51257
  return null;
51092
51258
  }
@@ -51127,11 +51293,11 @@ function parseManifestInputs(raw) {
51127
51293
  function readNodeSkill(agent, command, agentsDir = AGENTS_DIR) {
51128
51294
  const safe = (n) => !/[/\\\0]/.test(n) && !n.includes("..");
51129
51295
  if (!safe(agent) || !safe(command)) return null;
51130
- const manifestPath = (0, import_node_path2.join)(agentsDir, agent, "manifest.yaml");
51131
- if (!(0, import_node_fs3.existsSync)(manifestPath)) return null;
51296
+ const manifestPath = (0, import_node_path4.join)(agentsDir, agent, "manifest.yaml");
51297
+ if (!(0, import_node_fs5.existsSync)(manifestPath)) return null;
51132
51298
  let doc;
51133
51299
  try {
51134
- doc = asRecord((0, import_yaml.parse)((0, import_node_fs3.readFileSync)(manifestPath, "utf8")));
51300
+ doc = asRecord((0, import_yaml.parse)((0, import_node_fs5.readFileSync)(manifestPath, "utf8")));
51135
51301
  } catch {
51136
51302
  return null;
51137
51303
  }
@@ -51165,13 +51331,13 @@ function detectTriggerSource(nodes, connections, lookup) {
51165
51331
  if (!spec || spec.lifecycle !== "start" || !spec.streaming) return null;
51166
51332
  return { nodeId: src.id, agent: src.agent, command: src.command, inputs: spec.inputs };
51167
51333
  }
51168
- var AGENTS_DIR = process.env.AWARE_HOME ? (0, import_node_path2.join)(process.env.AWARE_HOME, "agents") : (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".aware", "agents");
51169
- var CREDENTIALS_DIR = process.env.AWARE_HOME ? (0, import_node_path2.join)(process.env.AWARE_HOME, "credentials") : (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".aware", "credentials");
51334
+ var AGENTS_DIR = process.env.AWARE_HOME ? (0, import_node_path4.join)(process.env.AWARE_HOME, "agents") : (0, import_node_path4.join)((0, import_node_os4.homedir)(), ".aware", "agents");
51335
+ var CREDENTIALS_DIR = process.env.AWARE_HOME ? (0, import_node_path4.join)(process.env.AWARE_HOME, "credentials") : (0, import_node_path4.join)((0, import_node_os4.homedir)(), ".aware", "credentials");
51170
51336
  function credentialStatus(secret) {
51171
- const file = (0, import_node_path2.join)(CREDENTIALS_DIR, `${secret}.json`);
51172
- if (!(0, import_node_fs3.existsSync)(file)) return { status: "disconnected", expiresAt: null };
51337
+ const file = (0, import_node_path4.join)(CREDENTIALS_DIR, `${secret}.json`);
51338
+ if (!(0, import_node_fs5.existsSync)(file)) return { status: "disconnected", expiresAt: null };
51173
51339
  try {
51174
- const doc = JSON.parse((0, import_node_fs3.readFileSync)(file, "utf8"));
51340
+ const doc = JSON.parse((0, import_node_fs5.readFileSync)(file, "utf8"));
51175
51341
  const raw = doc["expires_at"] ?? doc["expiresAt"] ?? doc["expiry"];
51176
51342
  if (raw != null) {
51177
51343
  const ms = typeof raw === "number" ? raw * (raw < 1e12 ? 1e3 : 1) : Date.parse(String(raw));
@@ -51184,15 +51350,15 @@ function credentialStatus(secret) {
51184
51350
  return { status: "connected", expiresAt: null };
51185
51351
  }
51186
51352
  function listIntegrations() {
51187
- if (!(0, import_node_fs3.existsSync)(AGENTS_DIR)) return [];
51353
+ if (!(0, import_node_fs5.existsSync)(AGENTS_DIR)) return [];
51188
51354
  const out = [];
51189
51355
  const seen = /* @__PURE__ */ new Set();
51190
51356
  const safe = (n) => !/[/\\\0]/.test(n) && !n.includes("..");
51191
- for (const agentId of (0, import_node_fs3.readdirSync)(AGENTS_DIR)) {
51357
+ for (const agentId of (0, import_node_fs5.readdirSync)(AGENTS_DIR)) {
51192
51358
  if (!safe(agentId)) continue;
51193
- const manifestPath = (0, import_node_path2.join)(AGENTS_DIR, agentId, "manifest.yaml");
51194
- if (!(0, import_node_fs3.existsSync)(manifestPath)) continue;
51195
- const manifestText = (0, import_node_fs3.readFileSync)(manifestPath, "utf8");
51359
+ const manifestPath = (0, import_node_path4.join)(AGENTS_DIR, agentId, "manifest.yaml");
51360
+ if (!(0, import_node_fs5.existsSync)(manifestPath)) continue;
51361
+ const manifestText = (0, import_node_fs5.readFileSync)(manifestPath, "utf8");
51196
51362
  if (!manifestText.includes("secrets:")) continue;
51197
51363
  let doc;
51198
51364
  try {
@@ -51283,14 +51449,14 @@ var AwareUnsupportedError = class extends AwareError {
51283
51449
  };
51284
51450
  function resolveInvoker() {
51285
51451
  const envDir = process.env.AWARE_CLI_DIR;
51286
- if (envDir && (0, import_node_fs4.existsSync)((0, import_node_path3.join)(envDir, "scripts/bin/aware.js"))) {
51287
- return { head: [nodeExe(), (0, import_node_path3.join)(envDir, "scripts/bin/aware.js")], shell: false, pkgDir: envDir };
51452
+ if (envDir && (0, import_node_fs6.existsSync)((0, import_node_path5.join)(envDir, "scripts/bin/aware.js"))) {
51453
+ return { head: [nodeExe(), (0, import_node_path5.join)(envDir, "scripts/bin/aware.js")], shell: false, pkgDir: envDir };
51288
51454
  }
51289
51455
  try {
51290
51456
  const root = (0, import_node_child_process.execFileSync)("npm", ["root", "-g"], { encoding: "utf8", shell: process.platform === "win32", windowsHide: true, timeout: 1e4 }).trim();
51291
- const pkgDir = (0, import_node_path3.join)(root, "@aware-aeco", "cli");
51292
- const bin = (0, import_node_path3.join)(pkgDir, "scripts/bin/aware.js");
51293
- if ((0, import_node_fs4.existsSync)(bin)) return { head: [nodeExe(), bin], shell: false, pkgDir };
51457
+ const pkgDir = (0, import_node_path5.join)(root, "@aware-aeco", "cli");
51458
+ const bin = (0, import_node_path5.join)(pkgDir, "scripts/bin/aware.js");
51459
+ if ((0, import_node_fs6.existsSync)(bin)) return { head: [nodeExe(), bin], shell: false, pkgDir };
51294
51460
  } catch {
51295
51461
  }
51296
51462
  return { head: ["aware"], shell: process.platform === "win32", pkgDir: null };
@@ -51307,7 +51473,7 @@ function awareNpmVersion() {
51307
51473
  const invoker = currentInvoker();
51308
51474
  if (!invoker.pkgDir) return null;
51309
51475
  try {
51310
- const pkg = JSON.parse((0, import_node_fs4.readFileSync)((0, import_node_path3.join)(invoker.pkgDir, "package.json"), "utf8"));
51476
+ const pkg = JSON.parse((0, import_node_fs6.readFileSync)((0, import_node_path5.join)(invoker.pkgDir, "package.json"), "utf8"));
51311
51477
  return typeof pkg.version === "string" ? pkg.version : null;
51312
51478
  } catch {
51313
51479
  return null;
@@ -51396,10 +51562,10 @@ async function runJson(args) {
51396
51562
  function resolveTeklaBridge() {
51397
51563
  const invoker = currentInvoker();
51398
51564
  const candidates = [
51399
- (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".aware", "bridges", "aware-tekla.exe"),
51400
- invoker.pkgDir ? (0, import_node_path3.join)(invoker.pkgDir, "binaries", "aware-tekla.exe") : null
51565
+ (0, import_node_path5.join)((0, import_node_os5.homedir)(), ".aware", "bridges", "aware-tekla.exe"),
51566
+ invoker.pkgDir ? (0, import_node_path5.join)(invoker.pkgDir, "binaries", "aware-tekla.exe") : null
51401
51567
  ].filter((p) => !!p);
51402
- return candidates.find((p) => (0, import_node_fs4.existsSync)(p)) ?? null;
51568
+ return candidates.find((p) => (0, import_node_fs6.existsSync)(p)) ?? null;
51403
51569
  }
51404
51570
  function injectDebugger(code) {
51405
51571
  const lines = code.split(/\r?\n/);
@@ -51481,39 +51647,39 @@ function cancelActiveRun() {
51481
51647
  function isRunActive() {
51482
51648
  return activeRun !== null;
51483
51649
  }
51484
- var LOGS_DIR = process.env.AWARE_HOME ? (0, import_node_path3.join)(process.env.AWARE_HOME, "logs") : (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".aware", "logs");
51650
+ var LOGS_DIR = process.env.AWARE_HOME ? (0, import_node_path5.join)(process.env.AWARE_HOME, "logs") : (0, import_node_path5.join)((0, import_node_os5.homedir)(), ".aware", "logs");
51485
51651
  var TRIGGER_POLL_MS = 250;
51486
51652
  function parseRunHandle(stdout) {
51487
51653
  const m = stdout.match(/instance\s+([^,\s]+),\s*run-id\s+([0-9a-f-]{36})/i);
51488
51654
  return m ? { instance: m[1], runId: m[2] } : null;
51489
51655
  }
51490
51656
  function traceLogPath(id, instance, runId, logsRoot = LOGS_DIR) {
51491
- return (0, import_node_path3.join)(logsRoot, id, instance, `${runId}.jsonl`);
51657
+ return (0, import_node_path5.join)(logsRoot, id, instance, `${runId}.jsonl`);
51492
51658
  }
51493
51659
  function latestTracePath(id, logsRoot = LOGS_DIR) {
51494
- const appDir2 = (0, import_node_path3.join)(logsRoot, id);
51495
- if (!(0, import_node_fs4.existsSync)(appDir2)) return null;
51660
+ const appDir2 = (0, import_node_path5.join)(logsRoot, id);
51661
+ if (!(0, import_node_fs6.existsSync)(appDir2)) return null;
51496
51662
  let best = null;
51497
51663
  let instances;
51498
51664
  try {
51499
- instances = (0, import_node_fs4.readdirSync)(appDir2);
51665
+ instances = (0, import_node_fs6.readdirSync)(appDir2);
51500
51666
  } catch {
51501
51667
  return null;
51502
51668
  }
51503
51669
  for (const instance of instances) {
51504
- const instDir = (0, import_node_path3.join)(appDir2, instance);
51670
+ const instDir = (0, import_node_path5.join)(appDir2, instance);
51505
51671
  let entries;
51506
51672
  try {
51507
- if (!(0, import_node_fs4.statSync)(instDir).isDirectory()) continue;
51508
- entries = (0, import_node_fs4.readdirSync)(instDir);
51673
+ if (!(0, import_node_fs6.statSync)(instDir).isDirectory()) continue;
51674
+ entries = (0, import_node_fs6.readdirSync)(instDir);
51509
51675
  } catch {
51510
51676
  continue;
51511
51677
  }
51512
51678
  for (const f of entries) {
51513
51679
  if (!f.endsWith(".jsonl")) continue;
51514
- const p = (0, import_node_path3.join)(instDir, f);
51680
+ const p = (0, import_node_path5.join)(instDir, f);
51515
51681
  try {
51516
- const m = (0, import_node_fs4.statSync)(p).mtimeMs;
51682
+ const m = (0, import_node_fs6.statSync)(p).mtimeMs;
51517
51683
  if (!best || m > best.mtimeMs) best = { path: p, mtimeMs: m };
51518
51684
  } catch {
51519
51685
  }
@@ -51522,17 +51688,17 @@ function latestTracePath(id, logsRoot = LOGS_DIR) {
51522
51688
  return best ? best.path : null;
51523
51689
  }
51524
51690
  function readTail(path, maxBytes) {
51525
- const size = (0, import_node_fs4.statSync)(path).size;
51526
- if (size <= maxBytes) return { text: (0, import_node_fs4.readFileSync)(path, "utf8"), truncated: false };
51527
- const fd2 = (0, import_node_fs4.openSync)(path, "r");
51691
+ const size = (0, import_node_fs6.statSync)(path).size;
51692
+ if (size <= maxBytes) return { text: (0, import_node_fs6.readFileSync)(path, "utf8"), truncated: false };
51693
+ const fd2 = (0, import_node_fs6.openSync)(path, "r");
51528
51694
  try {
51529
51695
  const buf = Buffer.alloc(maxBytes);
51530
- const read = (0, import_node_fs4.readSync)(fd2, buf, 0, maxBytes, size - maxBytes);
51696
+ const read = (0, import_node_fs6.readSync)(fd2, buf, 0, maxBytes, size - maxBytes);
51531
51697
  const nl = buf.indexOf(10, 0);
51532
51698
  const start = nl >= 0 && nl + 1 <= read ? nl + 1 : 0;
51533
51699
  return { text: buf.subarray(start, read).toString("utf8"), truncated: true };
51534
51700
  } finally {
51535
- (0, import_node_fs4.closeSync)(fd2);
51701
+ (0, import_node_fs6.closeSync)(fd2);
51536
51702
  }
51537
51703
  }
51538
51704
  function readLatestTrace(id, logsRoot = LOGS_DIR, maxBytes) {
@@ -51540,7 +51706,7 @@ function readLatestTrace(id, logsRoot = LOGS_DIR, maxBytes) {
51540
51706
  if (!p) return null;
51541
51707
  let read;
51542
51708
  try {
51543
- read = maxBytes && maxBytes > 0 ? readTail(p, maxBytes) : { text: (0, import_node_fs4.readFileSync)(p, "utf8"), truncated: false };
51709
+ read = maxBytes && maxBytes > 0 ? readTail(p, maxBytes) : { text: (0, import_node_fs6.readFileSync)(p, "utf8"), truncated: false };
51544
51710
  } catch {
51545
51711
  return null;
51546
51712
  }
@@ -51575,7 +51741,7 @@ function startTrigger(id, opts = {}) {
51575
51741
  const readAndEmit = () => {
51576
51742
  if (!tracePath || !traceCb) return;
51577
51743
  try {
51578
- const txt = (0, import_node_fs4.readFileSync)(tracePath, "utf8");
51744
+ const txt = (0, import_node_fs6.readFileSync)(tracePath, "utf8");
51579
51745
  if (txt.length !== lastLen) {
51580
51746
  lastLen = txt.length;
51581
51747
  traceCb(parseTrace(txt));
@@ -51636,13 +51802,13 @@ function assertNodeId(id) {
51636
51802
  var SOURCE_EXT = /\.(flo|app|flow|aware)$/i;
51637
51803
  function assertSourcePath(p) {
51638
51804
  if (!SOURCE_EXT.test(p)) throw new AwareError(`source must be a .flo/.app file: ${JSON.stringify(p)}`);
51639
- if (!(0, import_node_fs4.existsSync)(p)) throw new AwareError(`source path not found: ${JSON.stringify(p)}`);
51640
- return (0, import_node_fs4.realpathSync)(p);
51805
+ if (!(0, import_node_fs6.existsSync)(p)) throw new AwareError(`source path not found: ${JSON.stringify(p)}`);
51806
+ return (0, import_node_fs6.realpathSync)(p);
51641
51807
  }
51642
51808
  function assertAppDir(p) {
51643
- if (!(0, import_node_fs4.existsSync)(p)) throw new AwareError(`app dir not found: ${JSON.stringify(p)}`);
51644
- const real = (0, import_node_fs4.realpathSync)(p);
51645
- if (!(0, import_node_fs4.statSync)(real).isDirectory()) throw new AwareError(`app dir is not a directory: ${JSON.stringify(p)}`);
51809
+ if (!(0, import_node_fs6.existsSync)(p)) throw new AwareError(`app dir not found: ${JSON.stringify(p)}`);
51810
+ const real = (0, import_node_fs6.realpathSync)(p);
51811
+ if (!(0, import_node_fs6.statSync)(real).isDirectory()) throw new AwareError(`app dir is not a directory: ${JSON.stringify(p)}`);
51646
51812
  return real;
51647
51813
  }
51648
51814
  var GRAFT_PKG_AT_VER_RE = /^[A-Za-z0-9][\w.@/-]{0,127}$/;
@@ -51663,8 +51829,8 @@ function assertSourceRef(kind, ref) {
51663
51829
  case "openapi":
51664
51830
  case "yard":
51665
51831
  if (GRAFT_URL_RE.test(r)) return r;
51666
- if (!(0, import_node_fs4.existsSync)(r)) throw new AwareError(`${kind}: file/dir not found: ${r}`);
51667
- return (0, import_node_fs4.realpathSync)(r);
51832
+ if (!(0, import_node_fs6.existsSync)(r)) throw new AwareError(`${kind}: file/dir not found: ${r}`);
51833
+ return (0, import_node_fs6.realpathSync)(r);
51668
51834
  case "nuget":
51669
51835
  case "npm":
51670
51836
  case "ruby":
@@ -51901,8 +52067,8 @@ var aware = {
51901
52067
  try {
51902
52068
  let ref;
51903
52069
  if (inputs !== void 0) {
51904
- inputsFile = (0, import_node_path3.join)((0, import_node_os3.tmpdir)(), `floless-agent-inputs-${(0, import_node_crypto2.randomUUID)()}.json`);
51905
- (0, import_node_fs4.writeFileSync)(inputsFile, JSON.stringify(inputs));
52070
+ inputsFile = (0, import_node_path5.join)((0, import_node_os5.tmpdir)(), `floless-agent-inputs-${(0, import_node_crypto3.randomUUID)()}.json`);
52071
+ (0, import_node_fs6.writeFileSync)(inputsFile, JSON.stringify(inputs));
51906
52072
  ref = `@${inputsFile}`;
51907
52073
  }
51908
52074
  const { data } = await runJson(agentInvokeArgv(agent, command, ref));
@@ -51918,7 +52084,7 @@ var aware = {
51918
52084
  } finally {
51919
52085
  if (inputsFile) {
51920
52086
  try {
51921
- (0, import_node_fs4.rmSync)(inputsFile);
52087
+ (0, import_node_fs6.rmSync)(inputsFile);
51922
52088
  } catch {
51923
52089
  }
51924
52090
  }
@@ -51953,7 +52119,7 @@ var aware = {
51953
52119
  if (code !== 0) throw new AwareError(`aware app compile failed (exit ${code})`, { stdout, stderr });
51954
52120
  const m = stdout.match(/→\s*(.+\.lock)\s*$/m);
51955
52121
  const lockPath = m?.[1]?.trim() ?? null;
51956
- const lockText = lockPath && (0, import_node_fs4.existsSync)(lockPath) ? (0, import_node_fs4.readFileSync)(lockPath, "utf8") : null;
52122
+ const lockText = lockPath && (0, import_node_fs6.existsSync)(lockPath) ? (0, import_node_fs6.readFileSync)(lockPath, "utf8") : null;
51957
52123
  return { lockPath, lockText, output: stdout.trim() };
51958
52124
  },
51959
52125
  /**
@@ -52114,7 +52280,7 @@ var aware = {
52114
52280
  if (code !== 0) throw new AwareError(`aware app run failed (exit ${code})`, { stdout, stderr });
52115
52281
  const runId = stdout.match(/run-id\s+([0-9a-f-]{36})/i)?.[1] ?? null;
52116
52282
  const tracePath = stdout.match(/trace at\s+(.+\.jsonl)\s*$/m)?.[1]?.trim() ?? null;
52117
- const traceText = tracePath && (0, import_node_fs4.existsSync)(tracePath) ? (0, import_node_fs4.readFileSync)(tracePath, "utf8") : null;
52283
+ const traceText = tracePath && (0, import_node_fs6.existsSync)(tracePath) ? (0, import_node_fs6.readFileSync)(tracePath, "utf8") : null;
52118
52284
  return { runId, tracePath, traceText, output: stdout.trim() };
52119
52285
  } finally {
52120
52286
  if (activeRun === reg2) activeRun = null;
@@ -52147,20 +52313,20 @@ var aware = {
52147
52313
  };
52148
52314
 
52149
52315
  // extensions-store.ts
52150
- var import_node_fs5 = require("node:fs");
52151
- var import_node_os4 = require("node:os");
52152
- var import_node_path4 = require("node:path");
52153
- var ROOT = process.env.FLOLESS_HOME ?? (0, import_node_path4.join)((0, import_node_os4.homedir)(), ".floless");
52154
- var uiDir = (0, import_node_path4.join)(ROOT, "ui");
52155
- var extensionsFile = (0, import_node_path4.join)(uiDir, "extensions.json");
52156
- var TIMELINE_FILE = (0, import_node_path4.join)(uiDir, "timeline.json");
52316
+ var import_node_fs7 = require("node:fs");
52317
+ var import_node_os6 = require("node:os");
52318
+ var import_node_path6 = require("node:path");
52319
+ var ROOT = process.env.FLOLESS_HOME ?? (0, import_node_path6.join)((0, import_node_os6.homedir)(), ".floless");
52320
+ var uiDir = (0, import_node_path6.join)(ROOT, "ui");
52321
+ var extensionsFile = (0, import_node_path6.join)(uiDir, "extensions.json");
52322
+ var TIMELINE_FILE = (0, import_node_path6.join)(uiDir, "timeline.json");
52157
52323
  var HISTORY_KEEP = 20;
52158
52324
  function ensureUiDir() {
52159
- if (!(0, import_node_fs5.existsSync)(uiDir)) (0, import_node_fs5.mkdirSync)(uiDir, { recursive: true });
52325
+ if (!(0, import_node_fs7.existsSync)(uiDir)) (0, import_node_fs7.mkdirSync)(uiDir, { recursive: true });
52160
52326
  }
52161
52327
  function readExtensionsText() {
52162
52328
  try {
52163
- return (0, import_node_fs5.readFileSync)(extensionsFile, "utf8");
52329
+ return (0, import_node_fs7.readFileSync)(extensionsFile, "utf8");
52164
52330
  } catch {
52165
52331
  return null;
52166
52332
  }
@@ -52191,7 +52357,7 @@ function panelsCountOf(text) {
52191
52357
  function loadTimeline() {
52192
52358
  let text;
52193
52359
  try {
52194
- text = (0, import_node_fs5.readFileSync)(TIMELINE_FILE, "utf8");
52360
+ text = (0, import_node_fs7.readFileSync)(TIMELINE_FILE, "utf8");
52195
52361
  } catch {
52196
52362
  return { cursor: -1, entries: [] };
52197
52363
  }
@@ -52215,19 +52381,19 @@ function loadTimeline() {
52215
52381
  function saveTimeline(t) {
52216
52382
  ensureUiDir();
52217
52383
  const tmp = `${TIMELINE_FILE}.${process.pid}.tmp`;
52218
- (0, import_node_fs5.writeFileSync)(tmp, JSON.stringify(t, null, 2));
52219
- (0, import_node_fs5.renameSync)(tmp, TIMELINE_FILE);
52384
+ (0, import_node_fs7.writeFileSync)(tmp, JSON.stringify(t, null, 2));
52385
+ (0, import_node_fs7.renameSync)(tmp, TIMELINE_FILE);
52220
52386
  }
52221
52387
  function writeExtensionsText(text) {
52222
52388
  ensureUiDir();
52223
52389
  const tmp = `${extensionsFile}.${process.pid}.tmp`;
52224
- (0, import_node_fs5.writeFileSync)(tmp, text);
52225
- (0, import_node_fs5.renameSync)(tmp, extensionsFile);
52390
+ (0, import_node_fs7.writeFileSync)(tmp, text);
52391
+ (0, import_node_fs7.renameSync)(tmp, extensionsFile);
52226
52392
  }
52227
52393
  function applyState(entry2) {
52228
52394
  if (entry2.content === null) {
52229
52395
  try {
52230
- (0, import_node_fs5.rmSync)(extensionsFile);
52396
+ (0, import_node_fs7.rmSync)(extensionsFile);
52231
52397
  } catch {
52232
52398
  }
52233
52399
  } else {
@@ -52421,13 +52587,13 @@ function runBootstrap(deps) {
52421
52587
  }
52422
52588
 
52423
52589
  // licensing.ts
52424
- var import_node_fs6 = require("node:fs");
52425
- var import_node_path5 = require("node:path");
52426
- var import_node_os5 = require("node:os");
52590
+ var import_node_fs8 = require("node:fs");
52591
+ var import_node_path7 = require("node:path");
52592
+ var import_node_os7 = require("node:os");
52427
52593
  var import_node_child_process2 = require("node:child_process");
52428
- var import_node_crypto3 = require("node:crypto");
52594
+ var import_node_crypto4 = require("node:crypto");
52429
52595
  var import_node_module2 = require("node:module");
52430
- var import_node_os6 = require("node:os");
52596
+ var import_node_os8 = require("node:os");
52431
52597
  var ENVS = {
52432
52598
  production: { apiBase: "https://api.floless.io/api", webBase: "https://floless.io" },
52433
52599
  staging: { apiBase: "https://api.stage.floless.io/api", webBase: "https://stage.floless.io" }
@@ -52481,26 +52647,26 @@ function isSea() {
52481
52647
  }
52482
52648
  function storeDir() {
52483
52649
  if (process.env.FLOLESS_LICENSE_DIR) return process.env.FLOLESS_LICENSE_DIR;
52484
- if (isSea() && process.env.LOCALAPPDATA) return (0, import_node_path5.join)(process.env.LOCALAPPDATA, "FlolessApp-data");
52485
- return (0, import_node_path5.join)((0, import_node_os5.homedir)(), ".aware");
52650
+ if (isSea() && process.env.LOCALAPPDATA) return (0, import_node_path7.join)(process.env.LOCALAPPDATA, "FlolessApp-data");
52651
+ return (0, import_node_path7.join)((0, import_node_os7.homedir)(), ".aware");
52486
52652
  }
52487
- var tokenPath = () => (0, import_node_path5.join)(storeDir(), "floless-license.json");
52488
- var cachePath = () => (0, import_node_path5.join)(storeDir(), "floless-license-cache.json");
52653
+ var tokenPath = () => (0, import_node_path7.join)(storeDir(), "floless-license.json");
52654
+ var cachePath = () => (0, import_node_path7.join)(storeDir(), "floless-license-cache.json");
52489
52655
  var _migrated = false;
52490
52656
  function migrateLegacyStore() {
52491
52657
  if (_migrated) return;
52492
52658
  _migrated = true;
52493
52659
  if (process.env.FLOLESS_LICENSE_DIR || !(isSea() && process.env.LOCALAPPDATA)) return;
52494
- const oldDir = (0, import_node_path5.join)(process.env.LOCALAPPDATA, "FlolessApp");
52660
+ const oldDir = (0, import_node_path7.join)(process.env.LOCALAPPDATA, "FlolessApp");
52495
52661
  const newDir = storeDir();
52496
52662
  if (oldDir === newDir) return;
52497
52663
  for (const f of ["floless-license.json", "floless-license-cache.json", "floless-install-id"]) {
52498
52664
  try {
52499
- const oldP = (0, import_node_path5.join)(oldDir, f);
52500
- const newP = (0, import_node_path5.join)(newDir, f);
52501
- if ((0, import_node_fs6.existsSync)(oldP) && !(0, import_node_fs6.existsSync)(newP)) {
52502
- (0, import_node_fs6.mkdirSync)(newDir, { recursive: true });
52503
- (0, import_node_fs6.copyFileSync)(oldP, newP);
52665
+ const oldP = (0, import_node_path7.join)(oldDir, f);
52666
+ const newP = (0, import_node_path7.join)(newDir, f);
52667
+ if ((0, import_node_fs8.existsSync)(oldP) && !(0, import_node_fs8.existsSync)(newP)) {
52668
+ (0, import_node_fs8.mkdirSync)(newDir, { recursive: true });
52669
+ (0, import_node_fs8.copyFileSync)(oldP, newP);
52504
52670
  }
52505
52671
  } catch {
52506
52672
  }
@@ -52508,16 +52674,16 @@ function migrateLegacyStore() {
52508
52674
  }
52509
52675
  function readJson(path) {
52510
52676
  try {
52511
- return (0, import_node_fs6.existsSync)(path) ? JSON.parse((0, import_node_fs6.readFileSync)(path, "utf8")) : null;
52677
+ return (0, import_node_fs8.existsSync)(path) ? JSON.parse((0, import_node_fs8.readFileSync)(path, "utf8")) : null;
52512
52678
  } catch {
52513
52679
  return null;
52514
52680
  }
52515
52681
  }
52516
52682
  function writeJson(path, value) {
52517
- (0, import_node_fs6.mkdirSync)(storeDir(), { recursive: true });
52518
- (0, import_node_fs6.writeFileSync)(path, JSON.stringify(value, null, 2), "utf8");
52683
+ (0, import_node_fs8.mkdirSync)(storeDir(), { recursive: true });
52684
+ (0, import_node_fs8.writeFileSync)(path, JSON.stringify(value, null, 2), "utf8");
52519
52685
  try {
52520
- (0, import_node_fs6.chmodSync)(path, 384);
52686
+ (0, import_node_fs8.chmodSync)(path, 384);
52521
52687
  } catch {
52522
52688
  }
52523
52689
  }
@@ -52657,7 +52823,7 @@ async function ensureSeat(token) {
52657
52823
  const res = await fetch(`${env().apiBase}/seats/claim`, {
52658
52824
  method: "POST",
52659
52825
  headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
52660
- body: JSON.stringify({ device_fingerprint: installId(), device_name: (0, import_node_os6.hostname)(), os_version: `${process.platform} ${process.arch}` })
52826
+ body: JSON.stringify({ device_fingerprint: installId(), device_name: (0, import_node_os8.hostname)(), os_version: `${process.platform} ${process.arch}` })
52661
52827
  });
52662
52828
  if (!res.ok) return;
52663
52829
  const b = await res.json();
@@ -52678,7 +52844,7 @@ async function seatHeartbeat() {
52678
52844
  try {
52679
52845
  const token = await ensureFreshToken().catch(() => null);
52680
52846
  if (!token) return;
52681
- const hmac = (0, import_node_crypto3.createHmac)("sha256", seat.hmacSecret).update(seat.nonce).digest("hex");
52847
+ const hmac = (0, import_node_crypto4.createHmac)("sha256", seat.hmacSecret).update(seat.nonce).digest("hex");
52682
52848
  let res;
52683
52849
  try {
52684
52850
  res = await fetch(`${env().apiBase}/seats/heartbeat`, {
@@ -52750,7 +52916,7 @@ function logout() {
52750
52916
  resetSeat();
52751
52917
  for (const p of [tokenPath(), cachePath()]) {
52752
52918
  try {
52753
- (0, import_node_fs6.rmSync)(p, { force: true });
52919
+ (0, import_node_fs8.rmSync)(p, { force: true });
52754
52920
  } catch {
52755
52921
  }
52756
52922
  }
@@ -52774,7 +52940,7 @@ function openBrowser(url) {
52774
52940
  (0, import_node_child_process2.spawn)(cmd, args, opts).unref();
52775
52941
  }
52776
52942
  async function startLogin() {
52777
- const sessionId = (0, import_node_crypto3.randomUUID)();
52943
+ const sessionId = (0, import_node_crypto4.randomUUID)();
52778
52944
  const loginUrl = `${env().webBase}/login?desktop=true&session=${sessionId}&app=floless`;
52779
52945
  openBrowser(loginUrl);
52780
52946
  void pollDesktopStatus(sessionId).catch(() => {
@@ -52807,20 +52973,20 @@ async function pollDesktopStatus(sessionId, timeoutMs = 18e4) {
52807
52973
  return false;
52808
52974
  }
52809
52975
  function installId() {
52810
- const p = (0, import_node_path5.join)(storeDir(), "floless-install-id");
52976
+ const p = (0, import_node_path7.join)(storeDir(), "floless-install-id");
52811
52977
  const existing = readJson(p);
52812
52978
  if (typeof existing === "string" && existing.length >= 80 && existing.length <= 100) {
52813
52979
  return existing;
52814
52980
  }
52815
- const id = (0, import_node_crypto3.createHash)("sha512").update((0, import_node_crypto3.randomUUID)()).digest("base64");
52981
+ const id = (0, import_node_crypto4.createHash)("sha512").update((0, import_node_crypto4.randomUUID)()).digest("base64");
52816
52982
  writeJson(p, id);
52817
52983
  return id;
52818
52984
  }
52819
52985
 
52820
52986
  // version.ts
52821
52987
  var import_node_module3 = require("node:module");
52822
- var import_node_path6 = require("node:path");
52823
- var import_node_fs7 = require("node:fs");
52988
+ var import_node_path8 = require("node:path");
52989
+ var import_node_fs9 = require("node:fs");
52824
52990
  function resolveVersion(s) {
52825
52991
  if (s.isSea) {
52826
52992
  const m = s.sqVersionXml?.match(/<version>([^<]+)<\/version>/);
@@ -52839,8 +53005,8 @@ function isSea2() {
52839
53005
  }
52840
53006
  function readSqVersionXml() {
52841
53007
  try {
52842
- const sq = (0, import_node_path6.join)((0, import_node_path6.dirname)(process.execPath), "sq.version");
52843
- return (0, import_node_fs7.existsSync)(sq) ? (0, import_node_fs7.readFileSync)(sq, "utf8") : null;
53008
+ const sq = (0, import_node_path8.join)((0, import_node_path8.dirname)(process.execPath), "sq.version");
53009
+ return (0, import_node_fs9.existsSync)(sq) ? (0, import_node_fs9.readFileSync)(sq, "utf8") : null;
52844
53010
  } catch {
52845
53011
  return null;
52846
53012
  }
@@ -52856,7 +53022,7 @@ function appVersion() {
52856
53022
  return resolveVersion({
52857
53023
  isSea: isSea2(),
52858
53024
  sqVersionXml: readSqVersionXml(),
52859
- define: true ? "0.56.0" : void 0,
53025
+ define: true ? "0.57.0" : void 0,
52860
53026
  pkgVersion: readPkgVersion()
52861
53027
  });
52862
53028
  }
@@ -52866,16 +53032,16 @@ function resolveChannel(s) {
52866
53032
  return "dev";
52867
53033
  }
52868
53034
  function appChannel() {
52869
- return resolveChannel({ isSea: isSea2(), define: true ? "0.56.0" : void 0 });
53035
+ return resolveChannel({ isSea: isSea2(), define: true ? "0.57.0" : void 0 });
52870
53036
  }
52871
53037
 
52872
53038
  // workflow-update.ts
52873
- var import_node_fs8 = require("node:fs");
52874
- var import_node_os7 = require("node:os");
52875
- var import_node_path7 = require("node:path");
53039
+ var import_node_fs10 = require("node:fs");
53040
+ var import_node_os9 = require("node:os");
53041
+ var import_node_path9 = require("node:path");
52876
53042
  var import_node_url = require("node:url");
52877
53043
  var import_yaml2 = __toESM(require_dist6(), 1);
52878
- var here = (0, import_node_path7.dirname)((0, import_node_url.fileURLToPath)(__import_meta_url));
53044
+ var here = (0, import_node_path9.dirname)((0, import_node_url.fileURLToPath)(__import_meta_url));
52879
53045
  function compareSemver(a, b) {
52880
53046
  const pa = String(a).split(".");
52881
53047
  const pb = String(b).split(".");
@@ -52890,24 +53056,35 @@ function changelogSince(changelog, installed) {
52890
53056
  return (changelog ?? []).filter((e) => e && compareSemver(String(e.version), installed) > 0);
52891
53057
  }
52892
53058
  function detectUpdates(installed, templates) {
53059
+ const byId = new Map(templates.filter((t) => t.publisher === "floless").map((t) => [t.id, t]));
52893
53060
  const out = [];
52894
- for (const t of templates) {
52895
- if (t.publisher !== "floless") continue;
52896
- const iv = installed[t.id];
52897
- if (iv == null) continue;
52898
- if (compareSemver(t.version, iv) <= 0) continue;
53061
+ for (const app of installed) {
53062
+ if (app.forked) continue;
53063
+ if (app.publisher != null && app.publisher !== "floless") continue;
53064
+ const t = byId.get(app.templateId);
53065
+ if (!t) continue;
53066
+ if (compareSemver(t.version, app.version) <= 0) continue;
52899
53067
  out.push({
52900
- id: t.id,
52901
- installed: iv,
53068
+ id: app.id,
53069
+ templateId: t.id,
53070
+ installed: app.version,
52902
53071
  available: t.version,
52903
53072
  module: t.module,
52904
- changelog: changelogSince(t.changelog, iv)
53073
+ changelog: changelogSince(t.changelog, app.version)
52905
53074
  });
52906
53075
  }
52907
53076
  return out;
52908
53077
  }
53078
+ function stampSourceAppId(sourcePath, id) {
53079
+ const doc = (0, import_yaml2.parseDocument)((0, import_node_fs10.readFileSync)(sourcePath, "utf8"));
53080
+ if (doc.errors.length > 0) {
53081
+ throw new Error(`stamp app id: source is not valid YAML: ${doc.errors[0]?.message ?? "parse error"}`);
53082
+ }
53083
+ doc.set("app", id);
53084
+ (0, import_node_fs10.writeFileSync)(sourcePath, doc.toString());
53085
+ }
52909
53086
  function readTemplateMeta(path) {
52910
- const doc = (0, import_yaml2.parse)((0, import_node_fs8.readFileSync)(path, "utf8")) ?? {};
53087
+ const doc = (0, import_yaml2.parse)((0, import_node_fs10.readFileSync)(path, "utf8")) ?? {};
52911
53088
  return {
52912
53089
  id: String(doc.app ?? ""),
52913
53090
  version: doc.version != null ? String(doc.version) : "0.0.0",
@@ -52921,24 +53098,24 @@ function templatePaths(roots) {
52921
53098
  const envDir = process.env.FLOLESS_TEMPLATES_DIR;
52922
53099
  const flatDirs = roots ?? [
52923
53100
  ...envDir ? [envDir] : [],
52924
- (0, import_node_path7.join)(here, "templates"),
53101
+ (0, import_node_path9.join)(here, "templates"),
52925
53102
  // bundled: dist/templates beside the cjs
52926
- (0, import_node_path7.join)(here, "dist", "templates"),
53103
+ (0, import_node_path9.join)(here, "dist", "templates"),
52927
53104
  // dev: the built bundle at server/dist
52928
- (0, import_node_path7.join)((0, import_node_path7.dirname)(process.execPath), "templates")
53105
+ (0, import_node_path9.join)((0, import_node_path9.dirname)(process.execPath), "templates")
52929
53106
  // SEA exe
52930
53107
  ];
52931
53108
  for (const d of flatDirs) {
52932
- if (!(0, import_node_fs8.existsSync)(d)) continue;
52933
- const flos = (0, import_node_fs8.readdirSync)(d).filter((f) => f.endsWith(".flo"));
52934
- if (flos.length) return flos.map((f) => (0, import_node_path7.join)(d, f));
53109
+ if (!(0, import_node_fs10.existsSync)(d)) continue;
53110
+ const flos = (0, import_node_fs10.readdirSync)(d).filter((f) => f.endsWith(".flo"));
53111
+ if (flos.length) return flos.map((f) => (0, import_node_path9.join)(d, f));
52935
53112
  }
52936
- for (const demos of [(0, import_node_path7.join)(here, "..", "demos"), (0, import_node_path7.join)(here, "..", "..", "demos")]) {
52937
- if (!(0, import_node_fs8.existsSync)(demos)) continue;
53113
+ for (const demos of [(0, import_node_path9.join)(here, "..", "demos"), (0, import_node_path9.join)(here, "..", "..", "demos")]) {
53114
+ if (!(0, import_node_fs10.existsSync)(demos)) continue;
52938
53115
  const out = [];
52939
- for (const dir of (0, import_node_fs8.readdirSync)(demos)) {
53116
+ for (const dir of (0, import_node_fs10.readdirSync)(demos)) {
52940
53117
  try {
52941
- for (const f of (0, import_node_fs8.readdirSync)((0, import_node_path7.join)(demos, dir))) if (f.endsWith(".flo")) out.push((0, import_node_path7.join)(demos, dir, f));
53118
+ for (const f of (0, import_node_fs10.readdirSync)((0, import_node_path9.join)(demos, dir))) if (f.endsWith(".flo")) out.push((0, import_node_path9.join)(demos, dir, f));
52942
53119
  } catch {
52943
53120
  }
52944
53121
  }
@@ -52958,19 +53135,19 @@ function loadTemplates(roots) {
52958
53135
  return out;
52959
53136
  }
52960
53137
  function backupDir(id, version, ts) {
52961
- const root = process.env.FLOLESS_HOME ?? (0, import_node_path7.join)((0, import_node_os7.homedir)(), ".floless");
53138
+ const root = process.env.FLOLESS_HOME ?? (0, import_node_path9.join)((0, import_node_os9.homedir)(), ".floless");
52962
53139
  const safe = (s) => {
52963
53140
  const c = String(s).replace(/[^0-9A-Za-z._-]/g, "_");
52964
53141
  return /^\.+$/.test(c) ? "_" : c || "_";
52965
53142
  };
52966
- return (0, import_node_path7.join)(root, "app-backups", safe(id), `${safe(version)}-${ts}`);
53143
+ return (0, import_node_path9.join)(root, "app-backups", safe(id), `${safe(version)}-${ts}`);
52967
53144
  }
52968
53145
 
52969
53146
  // oauth-presets.ts
52970
- var import_node_os8 = require("node:os");
52971
- var import_node_path8 = require("node:path");
53147
+ var import_node_os10 = require("node:os");
53148
+ var import_node_path10 = require("node:path");
52972
53149
  var import_node_net = __toESM(require("node:net"), 1);
52973
- var import_node_fs9 = require("node:fs");
53150
+ var import_node_fs11 = require("node:fs");
52974
53151
  var MANAGED_HEADER = "# managed by floless.app - do not edit";
52975
53152
  var OAUTH_PRESETS = {
52976
53153
  "trimble-connect": {
@@ -53000,7 +53177,7 @@ function managedProfileYaml(preset) {
53000
53177
  ].join("\n");
53001
53178
  }
53002
53179
  function oauthDir() {
53003
- return process.env.AWARE_HOME ? (0, import_node_path8.join)(process.env.AWARE_HOME, "oauth") : (0, import_node_path8.join)((0, import_node_os8.homedir)(), ".aware", "oauth");
53180
+ return process.env.AWARE_HOME ? (0, import_node_path10.join)(process.env.AWARE_HOME, "oauth") : (0, import_node_path10.join)((0, import_node_os10.homedir)(), ".aware", "oauth");
53004
53181
  }
53005
53182
  function isUpgradableLegacyProfile(existing, preset) {
53006
53183
  if (existing.startsWith(MANAGED_HEADER)) return false;
@@ -53014,15 +53191,15 @@ function ensureManagedProfile(id) {
53014
53191
  const preset = OAUTH_PRESETS[id];
53015
53192
  if (!preset) return "not-managed";
53016
53193
  const dir = oauthDir();
53017
- const file = (0, import_node_path8.join)(dir, `${id}.yaml`);
53018
- const existing = (0, import_node_fs9.existsSync)(file) ? (0, import_node_fs9.readFileSync)(file, "utf8") : null;
53194
+ const file = (0, import_node_path10.join)(dir, `${id}.yaml`);
53195
+ const existing = (0, import_node_fs11.existsSync)(file) ? (0, import_node_fs11.readFileSync)(file, "utf8") : null;
53019
53196
  if (existing !== null && !existing.startsWith(MANAGED_HEADER) && !isUpgradableLegacyProfile(existing, preset)) {
53020
53197
  return "skipped";
53021
53198
  }
53022
53199
  const desired = managedProfileYaml(preset);
53023
53200
  if (existing === desired) return "unchanged";
53024
- (0, import_node_fs9.mkdirSync)(dir, { recursive: true });
53025
- (0, import_node_fs9.writeFileSync)(file, desired, "utf8");
53201
+ (0, import_node_fs11.mkdirSync)(dir, { recursive: true });
53202
+ (0, import_node_fs11.writeFileSync)(file, desired, "utf8");
53026
53203
  return "written";
53027
53204
  }
53028
53205
  function isPortBindable(port, host = "127.0.0.1") {
@@ -53193,16 +53370,16 @@ function buildPreview(m, sourceKind, sourceRef, stagedRef) {
53193
53370
  }
53194
53371
 
53195
53372
  // graft-manifest-reader.ts
53196
- var import_node_fs10 = require("node:fs");
53197
- var import_node_path9 = require("node:path");
53373
+ var import_node_fs12 = require("node:fs");
53374
+ var import_node_path11 = require("node:path");
53198
53375
  var import_yaml4 = __toESM(require_dist6(), 1);
53199
53376
  var asRecord2 = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : {};
53200
53377
  function readStagedManifest(agentDir) {
53201
- const manifestPath = (0, import_node_path9.join)(agentDir, "manifest.yaml");
53202
- if (!(0, import_node_fs10.existsSync)(manifestPath)) return null;
53378
+ const manifestPath = (0, import_node_path11.join)(agentDir, "manifest.yaml");
53379
+ if (!(0, import_node_fs12.existsSync)(manifestPath)) return null;
53203
53380
  let doc;
53204
53381
  try {
53205
- doc = asRecord2((0, import_yaml4.parse)((0, import_node_fs10.readFileSync)(manifestPath, "utf8")));
53382
+ doc = asRecord2((0, import_yaml4.parse)((0, import_node_fs12.readFileSync)(manifestPath, "utf8")));
53206
53383
  } catch {
53207
53384
  return null;
53208
53385
  }
@@ -53232,10 +53409,10 @@ function readStagedManifest(agentDir) {
53232
53409
  }
53233
53410
  let skillCount = Array.isArray(doc.skills) ? doc.skills.length : 0;
53234
53411
  if (!skillCount) {
53235
- const skillsDir = (0, import_node_path9.join)(agentDir, "skills");
53236
- if ((0, import_node_fs10.existsSync)(skillsDir)) {
53412
+ const skillsDir = (0, import_node_path11.join)(agentDir, "skills");
53413
+ if ((0, import_node_fs12.existsSync)(skillsDir)) {
53237
53414
  try {
53238
- skillCount = (0, import_node_fs10.readdirSync)(skillsDir).filter((f) => f.endsWith(".md")).length;
53415
+ skillCount = (0, import_node_fs12.readdirSync)(skillsDir).filter((f) => f.endsWith(".md")).length;
53239
53416
  } catch {
53240
53417
  }
53241
53418
  }
@@ -53249,7 +53426,7 @@ function readStagedManifest(agentDir) {
53249
53426
  }
53250
53427
 
53251
53428
  // graft-stage-registry.ts
53252
- var import_node_fs11 = require("node:fs");
53429
+ var import_node_fs13 = require("node:fs");
53253
53430
  var TTL_MS = 30 * 60 * 1e3;
53254
53431
  var registry = /* @__PURE__ */ new Map();
53255
53432
  function registerStage(token, tempDir, agentId) {
@@ -53271,7 +53448,7 @@ function evict(token) {
53271
53448
  clearTimeout(e.timer);
53272
53449
  registry.delete(token);
53273
53450
  try {
53274
- (0, import_node_fs11.rmSync)(e.tempDir, { recursive: true, force: true });
53451
+ (0, import_node_fs13.rmSync)(e.tempDir, { recursive: true, force: true });
53275
53452
  } catch {
53276
53453
  }
53277
53454
  }
@@ -53280,8 +53457,8 @@ function clearAllStages() {
53280
53457
  }
53281
53458
 
53282
53459
  // graft-commit.ts
53283
- var import_node_fs12 = require("node:fs");
53284
- var import_node_path10 = require("node:path");
53460
+ var import_node_fs14 = require("node:fs");
53461
+ var import_node_path12 = require("node:path");
53285
53462
  var GraftCommitError = class extends Error {
53286
53463
  constructor(message, code) {
53287
53464
  super(message);
@@ -53291,34 +53468,34 @@ var GraftCommitError = class extends Error {
53291
53468
  code;
53292
53469
  };
53293
53470
  function commitStaged(tempDir, agentId, targetAgentsDir, force) {
53294
- const staged = (0, import_node_path10.join)(tempDir, "agents", agentId);
53295
- const target = (0, import_node_path10.join)(targetAgentsDir, agentId);
53296
- if ((0, import_node_fs12.existsSync)(target)) {
53471
+ const staged = (0, import_node_path12.join)(tempDir, "agents", agentId);
53472
+ const target = (0, import_node_path12.join)(targetAgentsDir, agentId);
53473
+ if ((0, import_node_fs14.existsSync)(target)) {
53297
53474
  if (!force) throw new GraftCommitError(`an agent named "${agentId}" is already installed`, "collision");
53298
- (0, import_node_fs12.rmSync)(target, { recursive: true, force: true });
53475
+ (0, import_node_fs14.rmSync)(target, { recursive: true, force: true });
53299
53476
  }
53300
- (0, import_node_fs12.mkdirSync)(targetAgentsDir, { recursive: true });
53477
+ (0, import_node_fs14.mkdirSync)(targetAgentsDir, { recursive: true });
53301
53478
  try {
53302
- (0, import_node_fs12.renameSync)(staged, target);
53479
+ (0, import_node_fs14.renameSync)(staged, target);
53303
53480
  } catch {
53304
- (0, import_node_fs12.cpSync)(staged, target, { recursive: true });
53481
+ (0, import_node_fs14.cpSync)(staged, target, { recursive: true });
53305
53482
  }
53306
- (0, import_node_fs12.rmSync)(tempDir, { recursive: true, force: true });
53483
+ (0, import_node_fs14.rmSync)(tempDir, { recursive: true, force: true });
53307
53484
  return { agentId };
53308
53485
  }
53309
53486
  function matchAssemblies(globOrDir) {
53310
53487
  let dir;
53311
53488
  let pattern;
53312
- if ((0, import_node_fs12.existsSync)(globOrDir) && (0, import_node_fs12.statSync)(globOrDir).isDirectory()) {
53489
+ if ((0, import_node_fs14.existsSync)(globOrDir) && (0, import_node_fs14.statSync)(globOrDir).isDirectory()) {
53313
53490
  dir = globOrDir;
53314
53491
  pattern = "*.dll";
53315
53492
  } else {
53316
- dir = (0, import_node_path10.dirname)(globOrDir);
53317
- pattern = (0, import_node_path10.basename)(globOrDir);
53493
+ dir = (0, import_node_path12.dirname)(globOrDir);
53494
+ pattern = (0, import_node_path12.basename)(globOrDir);
53318
53495
  }
53319
- if (!dir || !(0, import_node_fs12.existsSync)(dir) || !(0, import_node_fs12.statSync)(dir).isDirectory()) return [];
53496
+ if (!dir || !(0, import_node_fs14.existsSync)(dir) || !(0, import_node_fs14.statSync)(dir).isDirectory()) return [];
53320
53497
  const re = globToRegExp(pattern);
53321
- return (0, import_node_fs12.readdirSync)(dir).filter((f) => re.test(f)).sort();
53498
+ return (0, import_node_fs14.readdirSync)(dir).filter((f) => re.test(f)).sort();
53322
53499
  }
53323
53500
  function globToRegExp(p) {
53324
53501
  const esc = p.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
@@ -53326,25 +53503,25 @@ function globToRegExp(p) {
53326
53503
  }
53327
53504
 
53328
53505
  // floless-store.ts
53329
- var import_node_crypto4 = require("node:crypto");
53330
- var import_node_fs13 = require("node:fs");
53331
- var import_node_os9 = require("node:os");
53332
- var import_node_path11 = require("node:path");
53333
- var ROOT2 = process.env.FLOLESS_HOME ?? (0, import_node_path11.join)((0, import_node_os9.homedir)(), ".floless");
53334
- var TEMPLATES_FILE = (0, import_node_path11.join)(ROOT2, "templates.json");
53335
- var REQUESTS_DIR = (0, import_node_path11.join)(ROOT2, "requests");
53506
+ var import_node_crypto5 = require("node:crypto");
53507
+ var import_node_fs15 = require("node:fs");
53508
+ var import_node_os11 = require("node:os");
53509
+ var import_node_path13 = require("node:path");
53510
+ var ROOT2 = process.env.FLOLESS_HOME ?? (0, import_node_path13.join)((0, import_node_os11.homedir)(), ".floless");
53511
+ var TEMPLATES_FILE = (0, import_node_path13.join)(ROOT2, "templates.json");
53512
+ var REQUESTS_DIR = (0, import_node_path13.join)(ROOT2, "requests");
53336
53513
  function ensureRoot() {
53337
- if (!(0, import_node_fs13.existsSync)(ROOT2)) (0, import_node_fs13.mkdirSync)(ROOT2, { recursive: true });
53514
+ if (!(0, import_node_fs15.existsSync)(ROOT2)) (0, import_node_fs15.mkdirSync)(ROOT2, { recursive: true });
53338
53515
  }
53339
53516
  function withinRequestsDir(p) {
53340
- const base = (0, import_node_path11.resolve)(REQUESTS_DIR);
53341
- const full = (0, import_node_path11.resolve)(p);
53342
- return full === base || full.startsWith(base + import_node_path11.sep);
53517
+ const base = (0, import_node_path13.resolve)(REQUESTS_DIR);
53518
+ const full = (0, import_node_path13.resolve)(p);
53519
+ return full === base || full.startsWith(base + import_node_path13.sep);
53343
53520
  }
53344
53521
  function listTemplates() {
53345
- if (!(0, import_node_fs13.existsSync)(TEMPLATES_FILE)) return [];
53522
+ if (!(0, import_node_fs15.existsSync)(TEMPLATES_FILE)) return [];
53346
53523
  try {
53347
- const parsed = JSON.parse((0, import_node_fs13.readFileSync)(TEMPLATES_FILE, "utf8"));
53524
+ const parsed = JSON.parse((0, import_node_fs15.readFileSync)(TEMPLATES_FILE, "utf8"));
53348
53525
  return Array.isArray(parsed) ? parsed : [];
53349
53526
  } catch {
53350
53527
  return [];
@@ -53353,8 +53530,8 @@ function listTemplates() {
53353
53530
  function writeTemplates(list) {
53354
53531
  ensureRoot();
53355
53532
  const tmp = `${TEMPLATES_FILE}.${process.pid}.tmp`;
53356
- (0, import_node_fs13.writeFileSync)(tmp, JSON.stringify(list, null, 2));
53357
- (0, import_node_fs13.renameSync)(tmp, TEMPLATES_FILE);
53533
+ (0, import_node_fs15.writeFileSync)(tmp, JSON.stringify(list, null, 2));
53534
+ (0, import_node_fs15.renameSync)(tmp, TEMPLATES_FILE);
53358
53535
  }
53359
53536
  function addTemplate(input) {
53360
53537
  const list = listTemplates();
@@ -53365,7 +53542,7 @@ function addTemplate(input) {
53365
53542
  if (dup) return dup;
53366
53543
  }
53367
53544
  const tpl = {
53368
- id: (0, import_node_crypto4.randomUUID)(),
53545
+ id: (0, import_node_crypto5.randomUUID)(),
53369
53546
  name: input.name.trim(),
53370
53547
  category: (input.category || "Uncategorized").trim(),
53371
53548
  node: input.node,
@@ -53386,12 +53563,12 @@ function deleteTemplate(id) {
53386
53563
  function getTemplate(id) {
53387
53564
  return listTemplates().find((t) => t.id === id) ?? null;
53388
53565
  }
53389
- function updateTemplate(id, patch) {
53566
+ function updateTemplate(id, patch2) {
53390
53567
  const list = listTemplates();
53391
53568
  const cur = list.find((t) => t.id === id);
53392
53569
  if (!cur) return null;
53393
- const name = patch.name?.trim();
53394
- const category = patch.category?.trim();
53570
+ const name = patch2.name?.trim();
53571
+ const category = patch2.category?.trim();
53395
53572
  cur.name = name || cur.name;
53396
53573
  cur.category = category || cur.category;
53397
53574
  writeTemplates(list);
@@ -53399,13 +53576,13 @@ function updateTemplate(id, patch) {
53399
53576
  }
53400
53577
  function addRequest(req, decoded = []) {
53401
53578
  ensureRoot();
53402
- if (!(0, import_node_fs13.existsSync)(REQUESTS_DIR)) (0, import_node_fs13.mkdirSync)(REQUESTS_DIR, { recursive: true });
53403
- const id = (0, import_node_crypto4.randomUUID)();
53579
+ if (!(0, import_node_fs15.existsSync)(REQUESTS_DIR)) (0, import_node_fs15.mkdirSync)(REQUESTS_DIR, { recursive: true });
53580
+ const id = (0, import_node_crypto5.randomUUID)();
53404
53581
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
53405
53582
  const base = `${createdAt.replace(/[:.]/g, "-")}__${id}`;
53406
53583
  const snapshots = decoded.map((d, i) => {
53407
- const p = (0, import_node_path11.join)(REQUESTS_DIR, `${base}__snap${i + 1}.${d.ext}`);
53408
- (0, import_node_fs13.writeFileSync)(p, d.buf);
53584
+ const p = (0, import_node_path13.join)(REQUESTS_DIR, `${base}__snap${i + 1}.${d.ext}`);
53585
+ (0, import_node_fs15.writeFileSync)(p, d.buf);
53409
53586
  return p;
53410
53587
  });
53411
53588
  const full = {
@@ -53415,55 +53592,55 @@ function addRequest(req, decoded = []) {
53415
53592
  ...req,
53416
53593
  ...snapshots.length ? { snapshots } : {}
53417
53594
  };
53418
- (0, import_node_fs13.writeFileSync)((0, import_node_path11.join)(REQUESTS_DIR, `${base}.json`), JSON.stringify(full, null, 2));
53595
+ (0, import_node_fs15.writeFileSync)((0, import_node_path13.join)(REQUESTS_DIR, `${base}.json`), JSON.stringify(full, null, 2));
53419
53596
  return full;
53420
53597
  }
53421
53598
  function listRequests() {
53422
- if (!(0, import_node_fs13.existsSync)(REQUESTS_DIR)) return [];
53423
- return (0, import_node_fs13.readdirSync)(REQUESTS_DIR).filter((f) => f.endsWith(".json")).sort().map((f) => {
53599
+ if (!(0, import_node_fs15.existsSync)(REQUESTS_DIR)) return [];
53600
+ return (0, import_node_fs15.readdirSync)(REQUESTS_DIR).filter((f) => f.endsWith(".json")).sort().map((f) => {
53424
53601
  try {
53425
- return JSON.parse((0, import_node_fs13.readFileSync)((0, import_node_path11.join)(REQUESTS_DIR, f), "utf8"));
53602
+ return JSON.parse((0, import_node_fs15.readFileSync)((0, import_node_path13.join)(REQUESTS_DIR, f), "utf8"));
53426
53603
  } catch {
53427
53604
  return null;
53428
53605
  }
53429
53606
  }).filter((r) => r !== null);
53430
53607
  }
53431
53608
  function deleteRequest(id) {
53432
- if (!(0, import_node_fs13.existsSync)(REQUESTS_DIR)) return false;
53433
- const file = (0, import_node_fs13.readdirSync)(REQUESTS_DIR).find((f) => f.endsWith(`__${id}.json`));
53609
+ if (!(0, import_node_fs15.existsSync)(REQUESTS_DIR)) return false;
53610
+ const file = (0, import_node_fs15.readdirSync)(REQUESTS_DIR).find((f) => f.endsWith(`__${id}.json`));
53434
53611
  if (!file) return false;
53435
53612
  try {
53436
- const parsed = JSON.parse((0, import_node_fs13.readFileSync)((0, import_node_path11.join)(REQUESTS_DIR, file), "utf8"));
53613
+ const parsed = JSON.parse((0, import_node_fs15.readFileSync)((0, import_node_path13.join)(REQUESTS_DIR, file), "utf8"));
53437
53614
  for (const p of parsed.snapshots ?? []) {
53438
53615
  if (!withinRequestsDir(p)) continue;
53439
53616
  try {
53440
- (0, import_node_fs13.rmSync)(p);
53617
+ (0, import_node_fs15.rmSync)(p);
53441
53618
  } catch {
53442
53619
  }
53443
53620
  }
53444
53621
  } catch {
53445
53622
  }
53446
- (0, import_node_fs13.rmSync)((0, import_node_path11.join)(REQUESTS_DIR, file));
53623
+ (0, import_node_fs15.rmSync)((0, import_node_path13.join)(REQUESTS_DIR, file));
53447
53624
  return true;
53448
53625
  }
53449
53626
  function clearRequests() {
53450
- if (!(0, import_node_fs13.existsSync)(REQUESTS_DIR)) return 0;
53451
- const allFiles = (0, import_node_fs13.readdirSync)(REQUESTS_DIR);
53627
+ if (!(0, import_node_fs15.existsSync)(REQUESTS_DIR)) return 0;
53628
+ const allFiles = (0, import_node_fs15.readdirSync)(REQUESTS_DIR);
53452
53629
  const jsonFiles = allFiles.filter((f) => f.endsWith(".json"));
53453
53630
  for (const f of allFiles) {
53454
53631
  try {
53455
- (0, import_node_fs13.rmSync)((0, import_node_path11.join)(REQUESTS_DIR, f));
53632
+ (0, import_node_fs15.rmSync)((0, import_node_path13.join)(REQUESTS_DIR, f));
53456
53633
  } catch {
53457
53634
  }
53458
53635
  }
53459
53636
  return jsonFiles.length;
53460
53637
  }
53461
53638
  function snapshotPathFor(id, n) {
53462
- if (!(0, import_node_fs13.existsSync)(REQUESTS_DIR)) return null;
53463
- const file = (0, import_node_fs13.readdirSync)(REQUESTS_DIR).find((f) => f.endsWith(`__${id}.json`));
53639
+ if (!(0, import_node_fs15.existsSync)(REQUESTS_DIR)) return null;
53640
+ const file = (0, import_node_fs15.readdirSync)(REQUESTS_DIR).find((f) => f.endsWith(`__${id}.json`));
53464
53641
  if (!file) return null;
53465
53642
  try {
53466
- const parsed = JSON.parse((0, import_node_fs13.readFileSync)((0, import_node_path11.join)(REQUESTS_DIR, file), "utf8"));
53643
+ const parsed = JSON.parse((0, import_node_fs15.readFileSync)((0, import_node_path13.join)(REQUESTS_DIR, file), "utf8"));
53467
53644
  const cand = parsed.snapshots?.[n] ?? null;
53468
53645
  return cand && withinRequestsDir(cand) ? cand : null;
53469
53646
  } catch {
@@ -53474,8 +53651,8 @@ var flolessRoot = ROOT2;
53474
53651
  var SnapshotError = class extends Error {
53475
53652
  };
53476
53653
  var MAX_SNAPSHOTS = 8;
53477
- var MAX_BYTES2 = 25 * 1024 * 1024;
53478
- function sniffExt(buf) {
53654
+ var MAX_BYTES3 = 25 * 1024 * 1024;
53655
+ function sniffExt2(buf) {
53479
53656
  if (buf.length >= 8 && buf[0] === 137 && buf[1] === 80 && buf[2] === 78 && buf[3] === 71) return "png";
53480
53657
  if (buf.length >= 3 && buf[0] === 255 && buf[1] === 216 && buf[2] === 255) return "jpg";
53481
53658
  if (buf.length >= 12 && buf.toString("ascii", 0, 4) === "RIFF" && buf.toString("ascii", 8, 12) === "WEBP") return "webp";
@@ -53488,100 +53665,24 @@ function decodeSnapshots(inputs) {
53488
53665
  return inputs.map((s) => {
53489
53666
  const m = /^data:([\w/+.-]+);base64,(.*)$/s.exec(s.dataUrl || "");
53490
53667
  if (!m) throw new SnapshotError("snapshot must be a base64 data URL");
53491
- if ((m[2]?.length ?? 0) * 0.75 > MAX_BYTES2) throw new SnapshotError("snapshot too large (max 25 MB)");
53668
+ if ((m[2]?.length ?? 0) * 0.75 > MAX_BYTES3) throw new SnapshotError("snapshot too large (max 25 MB)");
53492
53669
  const buf = Buffer.from(m[2] ?? "", "base64");
53493
53670
  if (buf.length === 0) throw new SnapshotError("empty snapshot");
53494
- if (buf.length > MAX_BYTES2) throw new SnapshotError("snapshot too large (max 25 MB)");
53495
- const ext = sniffExt(buf);
53671
+ if (buf.length > MAX_BYTES3) throw new SnapshotError("snapshot too large (max 25 MB)");
53672
+ const ext = sniffExt2(buf);
53496
53673
  if (!ext) throw new SnapshotError("snapshot must be PNG, JPEG, WebP, or PDF");
53497
53674
  return { buf, ext };
53498
53675
  });
53499
53676
  }
53500
53677
 
53501
- // visual-input-store.ts
53502
- var import_node_crypto5 = require("node:crypto");
53503
- var import_node_fs14 = require("node:fs");
53504
- var import_node_os10 = require("node:os");
53505
- var import_node_path12 = require("node:path");
53506
- var MAX_BYTES3 = 25 * 1024 * 1024;
53507
- var VisualInputError = class extends Error {
53508
- };
53509
- function inputsDir() {
53510
- const root = process.env.FLOLESS_HOME ?? (0, import_node_path12.join)((0, import_node_os10.homedir)(), ".floless");
53511
- return (0, import_node_path12.join)(root, "inputs");
53512
- }
53513
- function sniffExt2(buf) {
53514
- if (buf.length >= 8 && buf[0] === 137 && buf[1] === 80 && buf[2] === 78 && buf[3] === 71) return "png";
53515
- if (buf.length >= 3 && buf[0] === 255 && buf[1] === 216 && buf[2] === 255) return "jpg";
53516
- if (buf.length >= 12 && buf.toString("ascii", 0, 4) === "RIFF" && buf.toString("ascii", 8, 12) === "WEBP") return "webp";
53517
- if (buf.length >= 5 && buf.toString("ascii", 0, 5) === "%PDF-") return "pdf";
53518
- return null;
53519
- }
53520
- var RESERVED = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\..*)?$/i;
53521
- function safeSegment(s) {
53522
- if (!s || s === "." || s.includes("..")) return false;
53523
- if (s.includes("/") || s.includes("\\") || s.includes(import_node_path12.sep)) return false;
53524
- for (let i = 0; i < s.length; i++) {
53525
- const c = s.charCodeAt(i);
53526
- if (c < 32 || c === 58) return false;
53527
- }
53528
- if (/[. ]$/.test(s)) return false;
53529
- return !RESERVED.test(s);
53530
- }
53531
- function storeVisualInput(appId, dataUrl) {
53532
- if (!safeSegment(appId)) throw new VisualInputError("invalid app id");
53533
- const m = /^data:([\w/+.-]+);base64,(.*)$/s.exec(dataUrl || "");
53534
- if (!m) throw new VisualInputError("visual input must be a base64 data URL");
53535
- const buf = Buffer.from(m[2] ?? "", "base64");
53536
- if (buf.length === 0) throw new VisualInputError("empty visual input");
53537
- if (buf.length > MAX_BYTES3) throw new VisualInputError("visual input too large (max 25 MB)");
53538
- const ext = sniffExt2(buf);
53539
- if (!ext) throw new VisualInputError("visual input must be PNG, JPEG, WebP, or PDF");
53540
- const sha2562 = (0, import_node_crypto5.createHash)("sha256").update(buf).digest("hex");
53541
- const dir = (0, import_node_path12.join)(inputsDir(), appId);
53542
- const path = (0, import_node_path12.join)(dir, `${sha2562}.${ext}`);
53543
- const root = (0, import_node_path12.resolve)(inputsDir());
53544
- const target = (0, import_node_path12.resolve)(path);
53545
- if (target !== root && !target.startsWith(root + import_node_path12.sep)) {
53546
- throw new VisualInputError("refusing to write outside the inputs root");
53547
- }
53548
- (0, import_node_fs14.mkdirSync)(dir, { recursive: true });
53549
- if (!(0, import_node_fs14.existsSync)(path)) {
53550
- const tmp = `${path}.tmp`;
53551
- (0, import_node_fs14.writeFileSync)(tmp, buf);
53552
- (0, import_node_fs14.renameSync)(tmp, path);
53553
- }
53554
- return { path, ext, bytes: (0, import_node_fs14.statSync)(path).size, sha256: sha2562 };
53555
- }
53556
- function renameVisualInputs(oldId, newId) {
53557
- if (!safeSegment(oldId) || !safeSegment(newId)) throw new VisualInputError("invalid app id");
53558
- const from = (0, import_node_path12.join)(inputsDir(), oldId);
53559
- if (!(0, import_node_fs14.existsSync)(from)) return;
53560
- const to = (0, import_node_path12.join)(inputsDir(), newId);
53561
- if ((0, import_node_fs14.existsSync)(to)) (0, import_node_fs14.rmSync)(to, { recursive: true, force: true });
53562
- (0, import_node_fs14.mkdirSync)(inputsDir(), { recursive: true });
53563
- (0, import_node_fs14.renameSync)(from, to);
53564
- }
53565
- function copyVisualInputs(srcId, newId) {
53566
- if (!safeSegment(srcId) || !safeSegment(newId)) throw new VisualInputError("invalid app id");
53567
- const from = (0, import_node_path12.join)(inputsDir(), srcId);
53568
- if (!(0, import_node_fs14.existsSync)(from)) return;
53569
- (0, import_node_fs14.cpSync)(from, (0, import_node_path12.join)(inputsDir(), newId), { recursive: true });
53570
- }
53571
- function deleteVisualInputs(id) {
53572
- if (!safeSegment(id)) throw new VisualInputError("invalid app id");
53573
- const dir = (0, import_node_path12.join)(inputsDir(), id);
53574
- if ((0, import_node_fs14.existsSync)(dir)) (0, import_node_fs14.rmSync)(dir, { recursive: true, force: true });
53575
- }
53576
-
53577
53678
  // contract-store.ts
53578
- var import_node_fs16 = require("node:fs");
53579
- var import_node_path14 = require("node:path");
53580
- var import_node_os11 = require("node:os");
53679
+ var import_node_fs17 = require("node:fs");
53680
+ var import_node_path15 = require("node:path");
53681
+ var import_node_os12 = require("node:os");
53581
53682
 
53582
53683
  // contract-schema.ts
53583
- var import_node_fs15 = require("node:fs");
53584
- var import_node_path13 = require("node:path");
53684
+ var import_node_fs16 = require("node:fs");
53685
+ var import_node_path14 = require("node:path");
53585
53686
  var import_node_url2 = require("node:url");
53586
53687
  function validate(doc, schema) {
53587
53688
  const errors = [];
@@ -53682,16 +53783,16 @@ function walk(value, node, root, path, errors) {
53682
53783
  var _cached = null;
53683
53784
  function loadSteelTakeoffSchema() {
53684
53785
  if (_cached) return _cached;
53685
- const here2 = (0, import_node_path13.dirname)((0, import_node_url2.fileURLToPath)(__import_meta_url));
53786
+ const here2 = (0, import_node_path14.dirname)((0, import_node_url2.fileURLToPath)(__import_meta_url));
53686
53787
  const candidates = [
53687
- (0, import_node_path13.join)(here2, "..", "schemas", "steel.takeoff.v1.schema.json"),
53788
+ (0, import_node_path14.join)(here2, "..", "schemas", "steel.takeoff.v1.schema.json"),
53688
53789
  // dev: server/ next to schemas/
53689
- (0, import_node_path13.join)(here2, "schemas", "steel.takeoff.v1.schema.json")
53790
+ (0, import_node_path14.join)(here2, "schemas", "steel.takeoff.v1.schema.json")
53690
53791
  // bundled: dist/ holds ./schemas
53691
53792
  ];
53692
53793
  for (const p of candidates) {
53693
53794
  try {
53694
- const text = (0, import_node_fs15.readFileSync)(p, "utf8");
53795
+ const text = (0, import_node_fs16.readFileSync)(p, "utf8");
53695
53796
  _cached = JSON.parse(text);
53696
53797
  return _cached;
53697
53798
  } catch (err2) {
@@ -53707,20 +53808,20 @@ function validateSteelTakeoff(doc) {
53707
53808
  // contract-store.ts
53708
53809
  var ContractError = class extends Error {
53709
53810
  };
53710
- var ROOT3 = process.env.FLOLESS_HOME ?? (0, import_node_path14.join)((0, import_node_os11.homedir)(), ".floless");
53711
- var DIR = (0, import_node_path14.join)(ROOT3, "contracts");
53811
+ var ROOT3 = process.env.FLOLESS_HOME ?? (0, import_node_path15.join)((0, import_node_os12.homedir)(), ".floless");
53812
+ var DIR = (0, import_node_path15.join)(ROOT3, "contracts");
53712
53813
  function safeId(appId) {
53713
53814
  if (!/^[a-z0-9][a-z0-9._-]*$/i.test(appId)) throw new ContractError(`invalid appId: ${appId}`);
53714
53815
  return appId;
53715
53816
  }
53716
53817
  function contractPath(appId) {
53717
- return (0, import_node_path14.join)(DIR, `${safeId(appId)}.json`);
53818
+ return (0, import_node_path15.join)(DIR, `${safeId(appId)}.json`);
53718
53819
  }
53719
53820
  function readContract(appId) {
53720
53821
  const p = contractPath(appId);
53721
- if (!(0, import_node_fs16.existsSync)(p)) return null;
53822
+ if (!(0, import_node_fs17.existsSync)(p)) return null;
53722
53823
  try {
53723
- return JSON.parse((0, import_node_fs16.readFileSync)(p, "utf8"));
53824
+ return JSON.parse((0, import_node_fs17.readFileSync)(p, "utf8"));
53724
53825
  } catch (e) {
53725
53826
  console.warn(`readContract: ignoring unreadable contract at ${p}: ${e instanceof Error ? e.message : e}`);
53726
53827
  return null;
@@ -53733,8 +53834,8 @@ function writeContract(appId, doc) {
53733
53834
  const first = res.errors.slice(0, 5).map((e) => `${e.path}: ${e.message}`).join("; ");
53734
53835
  throw new ContractError(`contract failed schema validation \u2014 ${first}`);
53735
53836
  }
53736
- if (!(0, import_node_fs16.existsSync)(DIR)) (0, import_node_fs16.mkdirSync)(DIR, { recursive: true });
53737
- (0, import_node_fs16.writeFileSync)(p, JSON.stringify(doc));
53837
+ if (!(0, import_node_fs17.existsSync)(DIR)) (0, import_node_fs17.mkdirSync)(DIR, { recursive: true });
53838
+ (0, import_node_fs17.writeFileSync)(p, JSON.stringify(doc));
53738
53839
  }
53739
53840
 
53740
53841
  // contract-resolve.ts
@@ -53756,10 +53857,10 @@ function readContractForApp(appId, readAppFn = readApp) {
53756
53857
  }
53757
53858
 
53758
53859
  // contract-bake.ts
53759
- var import_node_fs17 = require("node:fs");
53860
+ var import_node_fs18 = require("node:fs");
53760
53861
  var import_yaml5 = __toESM(require_dist6(), 1);
53761
53862
  function bakeContractIntoApp(sourcePath, contract) {
53762
- const doc = (0, import_yaml5.parseDocument)((0, import_node_fs17.readFileSync)(sourcePath, "utf8"));
53863
+ const doc = (0, import_yaml5.parseDocument)((0, import_node_fs18.readFileSync)(sourcePath, "utf8"));
53763
53864
  if (doc.errors.length > 0) {
53764
53865
  throw new Error(`contract bake: source is not valid YAML: ${doc.errors[0]?.message ?? "parse error"}`);
53765
53866
  }
@@ -53802,7 +53903,7 @@ function bakeContractIntoApp(sourcePath, contract) {
53802
53903
  baked.filter = next;
53803
53904
  }
53804
53905
  doc.setIn(["nodes", idx, "config", "takeoff"], baked);
53805
- (0, import_node_fs17.writeFileSync)(sourcePath, doc.toString());
53906
+ (0, import_node_fs18.writeFileSync)(sourcePath, doc.toString());
53806
53907
  }
53807
53908
 
53808
53909
  // steel-joints.ts
@@ -54374,11 +54475,11 @@ function contractToBom(contractInput) {
54374
54475
  }
54375
54476
 
54376
54477
  // bom-export.ts
54377
- var import_node_os12 = require("node:os");
54378
- var import_node_path15 = require("node:path");
54478
+ var import_node_os13 = require("node:os");
54479
+ var import_node_path16 = require("node:path");
54379
54480
 
54380
54481
  // node_modules/write-excel-file/modules/export/writeXlsxFileNode.js
54381
- var import_node_fs18 = __toESM(require("node:fs"), 1);
54482
+ var import_node_fs19 = __toESM(require("node:fs"), 1);
54382
54483
 
54383
54484
  // node_modules/write-excel-file/modules/xlsx/helpers/features/getAdditionalContent.js
54384
54485
  function _createForOfIteratorHelperLoose(r, e) {
@@ -58550,7 +58651,7 @@ function writeXlsxFile(arg1, arg2, arg3) {
58550
58651
  },
58551
58652
  toFile: function toFile(filePath) {
58552
58653
  return createReadableStream().then(function(readableStream) {
58553
- return pipe(readableStream, import_node_fs18.default.createWriteStream(filePath));
58654
+ return pipe(readableStream, import_node_fs19.default.createWriteStream(filePath));
58554
58655
  });
58555
58656
  }
58556
58657
  };
@@ -58575,8 +58676,8 @@ function pipe(readableStream, writableStream) {
58575
58676
 
58576
58677
  // bom-export.ts
58577
58678
  function bomExportPath(appId, format) {
58578
- const root = process.env.FLOLESS_HOME ?? (0, import_node_path15.join)((0, import_node_os12.homedir)(), ".floless");
58579
- return (0, import_node_path15.join)(root, "exports", appId, `${appId}-bom.${format}`);
58679
+ const root = process.env.FLOLESS_HOME ?? (0, import_node_path16.join)((0, import_node_os13.homedir)(), ".floless");
58680
+ return (0, import_node_path16.join)(root, "exports", appId, `${appId}-bom.${format}`);
58580
58681
  }
58581
58682
  function csvField(v) {
58582
58683
  let s = String(v);
@@ -58604,11 +58705,11 @@ async function bomToXlsx(bom) {
58604
58705
  }
58605
58706
 
58606
58707
  // scene-bake.ts
58607
- var import_node_fs19 = require("node:fs");
58608
- var import_node_path16 = require("node:path");
58708
+ var import_node_fs20 = require("node:fs");
58709
+ var import_node_path17 = require("node:path");
58609
58710
  var import_yaml6 = __toESM(require_dist6(), 1);
58610
58711
  function bakeSceneIntoApp(sourcePath, scene, agent = "viewer-3d") {
58611
- const doc = (0, import_yaml6.parseDocument)((0, import_node_fs19.readFileSync)(sourcePath, "utf8"));
58712
+ const doc = (0, import_yaml6.parseDocument)((0, import_node_fs20.readFileSync)(sourcePath, "utf8"));
58612
58713
  if (doc.errors.length > 0) {
58613
58714
  throw new Error(`scene bake: source is not valid YAML: ${doc.errors[0]?.message ?? "parse error"}`);
58614
58715
  }
@@ -58617,10 +58718,10 @@ function bakeSceneIntoApp(sourcePath, scene, agent = "viewer-3d") {
58617
58718
  const idx = items.findIndex((it) => ((0, import_yaml6.isMap)(it) ? it.toJSON() : null)?.agent === agent);
58618
58719
  if (idx < 0) throw new Error(`no ${agent} render node to bake the scene into`);
58619
58720
  doc.setIn(["nodes", idx, "config", "scene"], scene);
58620
- (0, import_node_fs19.writeFileSync)(sourcePath, doc.toString());
58721
+ (0, import_node_fs20.writeFileSync)(sourcePath, doc.toString());
58621
58722
  }
58622
- function bakeNodeConfigById(sourcePath, nodeId, patch) {
58623
- const doc = (0, import_yaml6.parseDocument)((0, import_node_fs19.readFileSync)(sourcePath, "utf8"));
58723
+ function bakeNodeConfigById(sourcePath, nodeId, patch2) {
58724
+ const doc = (0, import_yaml6.parseDocument)((0, import_node_fs20.readFileSync)(sourcePath, "utf8"));
58624
58725
  if (doc.errors.length > 0) {
58625
58726
  throw new Error(`node bake: source is not valid YAML: ${doc.errors[0]?.message ?? "parse error"}`);
58626
58727
  }
@@ -58628,13 +58729,13 @@ function bakeNodeConfigById(sourcePath, nodeId, patch) {
58628
58729
  const items = (0, import_yaml6.isSeq)(seq) ? seq.items : [];
58629
58730
  const idx = items.findIndex((it) => ((0, import_yaml6.isMap)(it) ? it.toJSON() : null)?.id === nodeId);
58630
58731
  if (idx < 0) throw new Error(`no node with id "${nodeId}"`);
58631
- for (const [key, value] of Object.entries(patch)) doc.setIn(["nodes", idx, "config", key], value);
58632
- (0, import_node_fs19.writeFileSync)(sourcePath, doc.toString());
58732
+ for (const [key, value] of Object.entries(patch2)) doc.setIn(["nodes", idx, "config", key], value);
58733
+ (0, import_node_fs20.writeFileSync)(sourcePath, doc.toString());
58633
58734
  }
58634
58735
  function writeViewer3dApp(dir, appId, scene) {
58635
- (0, import_node_fs19.mkdirSync)(dir, { recursive: true });
58636
- const flo = (0, import_node_path16.join)(dir, `${appId}.flo`);
58637
- (0, import_node_fs19.writeFileSync)(flo, [
58736
+ (0, import_node_fs20.mkdirSync)(dir, { recursive: true });
58737
+ const flo = (0, import_node_path17.join)(dir, `${appId}.flo`);
58738
+ (0, import_node_fs20.writeFileSync)(flo, [
58638
58739
  `app: ${appId}`,
58639
58740
  "version: 0.1.0",
58640
58741
  "display-name: Steel 3D",
@@ -58655,9 +58756,9 @@ function writeViewer3dApp(dir, appId, scene) {
58655
58756
  return flo;
58656
58757
  }
58657
58758
  function writeIfcApp(dir, appId, scene, outputPath) {
58658
- (0, import_node_fs19.mkdirSync)(dir, { recursive: true });
58659
- const flo = (0, import_node_path16.join)(dir, `${appId}.flo`);
58660
- (0, import_node_fs19.writeFileSync)(flo, [
58759
+ (0, import_node_fs20.mkdirSync)(dir, { recursive: true });
58760
+ const flo = (0, import_node_path17.join)(dir, `${appId}.flo`);
58761
+ (0, import_node_fs20.writeFileSync)(flo, [
58661
58762
  `app: ${appId}`,
58662
58763
  "version: 0.1.0",
58663
58764
  "display-name: Steel IFC",
@@ -58680,9 +58781,9 @@ function writeIfcApp(dir, appId, scene, outputPath) {
58680
58781
  return flo;
58681
58782
  }
58682
58783
  function writeTeklaApp(dir, appId, scene, teklaVersion = "2026.0") {
58683
- (0, import_node_fs19.mkdirSync)(dir, { recursive: true });
58684
- const flo = (0, import_node_path16.join)(dir, `${appId}.flo`);
58685
- (0, import_node_fs19.writeFileSync)(flo, [
58784
+ (0, import_node_fs20.mkdirSync)(dir, { recursive: true });
58785
+ const flo = (0, import_node_path17.join)(dir, `${appId}.flo`);
58786
+ (0, import_node_fs20.writeFileSync)(flo, [
58686
58787
  `app: ${appId}`,
58687
58788
  "version: 0.1.0",
58688
58789
  "display-name: Steel to Tekla",
@@ -58721,11 +58822,11 @@ async function bakeBomExportNodes(sourcePath, appId, contract) {
58721
58822
  xlsx: bom ? (await bomToXlsx(bom)).toString("base64") : null
58722
58823
  };
58723
58824
  for (const { id, format } of EXPORT_NODES) {
58724
- const patch = { path: bomExportPath(appId, format) };
58825
+ const patch2 = { path: bomExportPath(appId, format) };
58725
58826
  const bytes = bytesFor[format];
58726
- if (bytes != null) patch.bytes = bytes;
58827
+ if (bytes != null) patch2.bytes = bytes;
58727
58828
  try {
58728
- bakeNodeConfigById(sourcePath, id, patch);
58829
+ bakeNodeConfigById(sourcePath, id, patch2);
58729
58830
  baked.push(id);
58730
58831
  } catch (e) {
58731
58832
  if (!(e instanceof Error && /no node with id/.test(e.message))) throw e;
@@ -59081,13 +59182,13 @@ function scoreContract2(contractInput) {
59081
59182
  }
59082
59183
 
59083
59184
  // app-lifecycle.ts
59084
- var import_node_fs22 = require("node:fs");
59085
- var import_node_os14 = require("node:os");
59086
- var import_node_path19 = require("node:path");
59185
+ var import_node_fs23 = require("node:fs");
59186
+ var import_node_os15 = require("node:os");
59187
+ var import_node_path20 = require("node:path");
59087
59188
 
59088
59189
  // routines.ts
59089
- var import_node_fs21 = require("node:fs");
59090
- var import_node_path18 = require("node:path");
59190
+ var import_node_fs22 = require("node:fs");
59191
+ var import_node_path19 = require("node:path");
59091
59192
 
59092
59193
  // sse.ts
59093
59194
  var clients = /* @__PURE__ */ new Set();
@@ -59124,11 +59225,11 @@ function clientCount() {
59124
59225
  }
59125
59226
 
59126
59227
  // trigger-sessions.ts
59127
- var import_node_fs20 = require("node:fs");
59128
- var import_node_os13 = require("node:os");
59129
- var import_node_path17 = require("node:path");
59228
+ var import_node_fs21 = require("node:fs");
59229
+ var import_node_os14 = require("node:os");
59230
+ var import_node_path18 = require("node:path");
59130
59231
  var import_yaml7 = __toESM(require_dist6(), 1);
59131
- var AGENTS_DIR2 = process.env.AWARE_HOME ? (0, import_node_path17.join)(process.env.AWARE_HOME, "agents") : (0, import_node_path17.join)((0, import_node_os13.homedir)(), ".aware", "agents");
59232
+ var AGENTS_DIR2 = process.env.AWARE_HOME ? (0, import_node_path18.join)(process.env.AWARE_HOME, "agents") : (0, import_node_path18.join)((0, import_node_os14.homedir)(), ".aware", "agents");
59132
59233
  function summarizeFire(data) {
59133
59234
  if (!data) return "event";
59134
59235
  const type = typeof data.type === "string" ? data.type : "";
@@ -59145,10 +59246,10 @@ function mapTriggerState(phase) {
59145
59246
  function isHostBacked(agent, agentsDir = AGENTS_DIR2) {
59146
59247
  const safe = (n) => !n.includes("/") && !n.includes("\\") && !n.includes("..");
59147
59248
  if (!safe(agent)) return false;
59148
- const manifestPath = (0, import_node_path17.join)(agentsDir, agent, "manifest.yaml");
59149
- if (!(0, import_node_fs20.existsSync)(manifestPath)) return false;
59249
+ const manifestPath = (0, import_node_path18.join)(agentsDir, agent, "manifest.yaml");
59250
+ if (!(0, import_node_fs21.existsSync)(manifestPath)) return false;
59150
59251
  try {
59151
- const doc = (0, import_yaml7.parse)((0, import_node_fs20.readFileSync)(manifestPath, "utf8"));
59252
+ const doc = (0, import_yaml7.parse)((0, import_node_fs21.readFileSync)(manifestPath, "utf8"));
59152
59253
  const transport = doc?.transport;
59153
59254
  return !!(transport && typeof transport === "object" && "cli" in transport);
59154
59255
  } catch {
@@ -59433,7 +59534,7 @@ var RoutineError = class extends Error {
59433
59534
  }
59434
59535
  status;
59435
59536
  };
59436
- var ROUTINES_FILE = (0, import_node_path18.join)(flolessRoot, "routines.json");
59537
+ var ROUTINES_FILE = (0, import_node_path19.join)(flolessRoot, "routines.json");
59437
59538
  var routines = [];
59438
59539
  var loaded = false;
59439
59540
  function ensureLoaded() {
@@ -59469,10 +59570,10 @@ function sanitizeRoutine(raw) {
59469
59570
  };
59470
59571
  }
59471
59572
  function loadFromDisk() {
59472
- if (!(0, import_node_fs21.existsSync)(ROUTINES_FILE)) return [];
59573
+ if (!(0, import_node_fs22.existsSync)(ROUTINES_FILE)) return [];
59473
59574
  let text;
59474
59575
  try {
59475
- text = (0, import_node_fs21.readFileSync)(ROUTINES_FILE, "utf8");
59576
+ text = (0, import_node_fs22.readFileSync)(ROUTINES_FILE, "utf8");
59476
59577
  } catch {
59477
59578
  return [];
59478
59579
  }
@@ -59481,17 +59582,17 @@ function loadFromDisk() {
59481
59582
  return Array.isArray(parsed) ? parsed.map(sanitizeRoutine).filter((r) => r !== null) : [];
59482
59583
  } catch {
59483
59584
  try {
59484
- (0, import_node_fs21.renameSync)(ROUTINES_FILE, `${ROUTINES_FILE}.corrupt-${Date.now()}`);
59585
+ (0, import_node_fs22.renameSync)(ROUTINES_FILE, `${ROUTINES_FILE}.corrupt-${Date.now()}`);
59485
59586
  } catch {
59486
59587
  }
59487
59588
  return [];
59488
59589
  }
59489
59590
  }
59490
59591
  function saveRoutines() {
59491
- if (!(0, import_node_fs21.existsSync)(flolessRoot)) (0, import_node_fs21.mkdirSync)(flolessRoot, { recursive: true });
59592
+ if (!(0, import_node_fs22.existsSync)(flolessRoot)) (0, import_node_fs22.mkdirSync)(flolessRoot, { recursive: true });
59492
59593
  const tmp = `${ROUTINES_FILE}.${process.pid}.tmp`;
59493
- (0, import_node_fs21.writeFileSync)(tmp, JSON.stringify(routines, null, 2));
59494
- (0, import_node_fs21.renameSync)(tmp, ROUTINES_FILE);
59594
+ (0, import_node_fs22.writeFileSync)(tmp, JSON.stringify(routines, null, 2));
59595
+ (0, import_node_fs22.renameSync)(tmp, ROUTINES_FILE);
59495
59596
  }
59496
59597
  function listRoutines() {
59497
59598
  ensureLoaded();
@@ -59648,30 +59749,30 @@ function createRoutine(input) {
59648
59749
  broadcast({ type: "routine-changed", id: r.id });
59649
59750
  return r;
59650
59751
  }
59651
- function updateRoutine(id, patch) {
59752
+ function updateRoutine(id, patch2) {
59652
59753
  ensureLoaded();
59653
59754
  const r = getRoutine(id);
59654
59755
  if (!r) return null;
59655
- if (patch.name !== void 0) {
59656
- const name = String(patch.name).trim();
59756
+ if (patch2.name !== void 0) {
59757
+ const name = String(patch2.name).trim();
59657
59758
  if (!name) throw new RoutineError("name cannot be empty");
59658
59759
  r.name = name;
59659
59760
  }
59660
- if (patch.schedule !== void 0) {
59761
+ if (patch2.schedule !== void 0) {
59661
59762
  if (r.kind !== "schedule") throw new RoutineError("cannot set a schedule on a trigger routine");
59662
- const err2 = validateSchedule(patch.schedule);
59763
+ const err2 = validateSchedule(patch2.schedule);
59663
59764
  if (err2) throw new RoutineError(err2);
59664
- r.schedule = patch.schedule;
59765
+ r.schedule = patch2.schedule;
59665
59766
  }
59666
- if (patch.inputs !== void 0) {
59767
+ if (patch2.inputs !== void 0) {
59667
59768
  const app = requireApp(r.workflow);
59668
- r.inputs = coerceInputs(app, patch.inputs);
59769
+ r.inputs = coerceInputs(app, patch2.inputs);
59669
59770
  }
59670
- if (patch.enabled !== void 0) r.enabled = !!patch.enabled;
59771
+ if (patch2.enabled !== void 0) r.enabled = !!patch2.enabled;
59671
59772
  r.updated = (/* @__PURE__ */ new Date()).toISOString();
59672
59773
  r.nextFireAt = computeNextFire(r);
59673
59774
  saveRoutines();
59674
- if (r.kind === "trigger" && (patch.enabled !== void 0 || patch.inputs !== void 0)) {
59775
+ if (r.kind === "trigger" && (patch2.enabled !== void 0 || patch2.inputs !== void 0)) {
59675
59776
  syncTriggerSession(r);
59676
59777
  }
59677
59778
  broadcast({ type: "routine-changed", id: r.id });
@@ -59899,13 +60000,13 @@ var AppLifecycleError = class extends Error {
59899
60000
  }
59900
60001
  };
59901
60002
  function appsDir() {
59902
- return process.env.AWARE_HOME ? (0, import_node_path19.join)(process.env.AWARE_HOME, "apps") : (0, import_node_path19.join)((0, import_node_os14.homedir)(), ".aware", "apps");
60003
+ return process.env.AWARE_HOME ? (0, import_node_path20.join)(process.env.AWARE_HOME, "apps") : (0, import_node_path20.join)((0, import_node_os15.homedir)(), ".aware", "apps");
59903
60004
  }
59904
60005
  function appDirPath(id) {
59905
- return (0, import_node_path19.join)(appsDir(), id);
60006
+ return (0, import_node_path20.join)(appsDir(), id);
59906
60007
  }
59907
60008
  function appInstalled(id) {
59908
- return APP_ID3.test(id) && (0, import_node_fs22.existsSync)(appDirPath(id));
60009
+ return APP_ID3.test(id) && (0, import_node_fs23.existsSync)(appDirPath(id));
59909
60010
  }
59910
60011
  function logCascade(what, e) {
59911
60012
  console.error(`[app-lifecycle] cascade step failed (${what}):`, e instanceof Error ? e.message : e);
@@ -59943,6 +60044,11 @@ async function renameApp(oldId, newId, deps = defaultDeps2) {
59943
60044
  } catch (e) {
59944
60045
  logCascade("re-point routines", e);
59945
60046
  }
60047
+ try {
60048
+ renameOrigin(oldId, newId);
60049
+ } catch (e) {
60050
+ logCascade("carry lineage", e);
60051
+ }
59946
60052
  return { id: newId, compiled };
59947
60053
  }
59948
60054
  async function duplicateApp(srcId, newId, deps = defaultDeps2) {
@@ -59955,6 +60061,11 @@ async function duplicateApp(srcId, newId, deps = defaultDeps2) {
59955
60061
  } catch (e) {
59956
60062
  logCascade("copy visual inputs", e);
59957
60063
  }
60064
+ try {
60065
+ copyOrigin(srcId, newId);
60066
+ } catch (e) {
60067
+ logCascade("copy lineage", e);
60068
+ }
59958
60069
  return { id: newId, compiled };
59959
60070
  }
59960
60071
  async function deleteApp(id, deps = defaultDeps2) {
@@ -59970,6 +60081,11 @@ async function deleteApp(id, deps = defaultDeps2) {
59970
60081
  } catch (e) {
59971
60082
  logCascade("remove routines", e);
59972
60083
  }
60084
+ try {
60085
+ deleteOrigin(id);
60086
+ } catch (e) {
60087
+ logCascade("drop lineage", e);
60088
+ }
59973
60089
  }
59974
60090
 
59975
60091
  // route-gate.ts
@@ -60006,9 +60122,9 @@ function isGatedAwareRoute(url, method) {
60006
60122
 
60007
60123
  // autostart.mjs
60008
60124
  var import_node_child_process4 = require("node:child_process");
60009
- var import_node_fs23 = require("node:fs");
60010
- var import_node_os15 = require("node:os");
60011
- var import_node_path20 = require("node:path");
60125
+ var import_node_fs24 = require("node:fs");
60126
+ var import_node_os16 = require("node:os");
60127
+ var import_node_path21 = require("node:path");
60012
60128
 
60013
60129
  // teardown.mjs
60014
60130
  var RUN_KEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
@@ -60120,8 +60236,8 @@ function removeLegacyRunKey() {
60120
60236
  }
60121
60237
  function logLine(msg) {
60122
60238
  try {
60123
- (0, import_node_fs23.mkdirSync)(logDir(), { recursive: true });
60124
- (0, import_node_fs23.appendFileSync)(logFilePath(), `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
60239
+ (0, import_node_fs24.mkdirSync)(logDir(), { recursive: true });
60240
+ (0, import_node_fs24.appendFileSync)(logFilePath(), `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
60125
60241
  `);
60126
60242
  } catch {
60127
60243
  }
@@ -60129,8 +60245,8 @@ function logLine(msg) {
60129
60245
  function registerAutostart(exePath) {
60130
60246
  if (!isWin) return;
60131
60247
  const xml = buildAutostartTaskXml(exePath, currentUserId());
60132
- const tmp = (0, import_node_path20.join)((0, import_node_os15.tmpdir)(), `floless-autostart-${process.pid}-${Date.now()}.xml`);
60133
- (0, import_node_fs23.writeFileSync)(tmp, "\uFEFF" + xml, { encoding: "utf16le" });
60248
+ const tmp = (0, import_node_path21.join)((0, import_node_os16.tmpdir)(), `floless-autostart-${process.pid}-${Date.now()}.xml`);
60249
+ (0, import_node_fs24.writeFileSync)(tmp, "\uFEFF" + xml, { encoding: "utf16le" });
60134
60250
  try {
60135
60251
  (0, import_node_child_process4.execFileSync)("schtasks", ["/Create", "/TN", TASK_NAME, "/XML", tmp, "/F"], {
60136
60252
  stdio: ["ignore", "ignore", "ignore"],
@@ -60141,7 +60257,7 @@ function registerAutostart(exePath) {
60141
60257
  throw err2;
60142
60258
  } finally {
60143
60259
  try {
60144
- (0, import_node_fs23.rmSync)(tmp, { force: true });
60260
+ (0, import_node_fs24.rmSync)(tmp, { force: true });
60145
60261
  } catch {
60146
60262
  }
60147
60263
  }
@@ -60176,26 +60292,26 @@ function unregisterAutostart() {
60176
60292
  // updater.ts
60177
60293
  var import_node_child_process5 = require("node:child_process");
60178
60294
  var import_node_crypto6 = require("node:crypto");
60179
- var import_node_fs25 = require("node:fs");
60295
+ var import_node_fs26 = require("node:fs");
60180
60296
  var import_node_stream3 = require("node:stream");
60181
60297
  var import_promises = require("node:stream/promises");
60182
- var import_node_path22 = require("node:path");
60298
+ var import_node_path23 = require("node:path");
60183
60299
 
60184
60300
  // post-update-marker.mjs
60185
- var import_node_fs24 = require("node:fs");
60186
- var import_node_os16 = require("node:os");
60187
- var import_node_path21 = require("node:path");
60301
+ var import_node_fs25 = require("node:fs");
60302
+ var import_node_os17 = require("node:os");
60303
+ var import_node_path22 = require("node:path");
60188
60304
  var FRESH_MS = 12e4;
60189
60305
  function markerPath() {
60190
60306
  const override = (process.env.FLOLESS_POST_UPDATE_MARKER ?? "").trim();
60191
60307
  if (override) return override;
60192
- const root = process.env.FLOLESS_HOME ?? (0, import_node_path21.join)((0, import_node_os16.homedir)(), ".floless");
60193
- return (0, import_node_path21.join)(root, ".post-update");
60308
+ const root = process.env.FLOLESS_HOME ?? (0, import_node_path22.join)((0, import_node_os17.homedir)(), ".floless");
60309
+ return (0, import_node_path22.join)(root, ".post-update");
60194
60310
  }
60195
60311
  function legacyMarkerPath() {
60196
60312
  if ((process.env.FLOLESS_POST_UPDATE_MARKER ?? "").trim()) return null;
60197
60313
  try {
60198
- return (0, import_node_path21.join)((0, import_node_path21.dirname)((0, import_node_path21.dirname)(process.execPath)), ".floless-post-update");
60314
+ return (0, import_node_path22.join)((0, import_node_path22.dirname)((0, import_node_path22.dirname)(process.execPath)), ".floless-post-update");
60199
60315
  } catch {
60200
60316
  return null;
60201
60317
  }
@@ -60205,7 +60321,7 @@ function writePostUpdateMarker() {
60205
60321
  for (const p of [markerPath(), legacyMarkerPath()]) {
60206
60322
  if (!p) continue;
60207
60323
  try {
60208
- (0, import_node_fs24.writeFileSync)(p, (/* @__PURE__ */ new Date()).toISOString());
60324
+ (0, import_node_fs25.writeFileSync)(p, (/* @__PURE__ */ new Date()).toISOString());
60209
60325
  wrote = true;
60210
60326
  } catch {
60211
60327
  }
@@ -60217,9 +60333,9 @@ function consumePostUpdateMarker() {
60217
60333
  for (const p of [markerPath(), legacyMarkerPath()]) {
60218
60334
  if (!p) continue;
60219
60335
  try {
60220
- if (!(0, import_node_fs24.existsSync)(p)) continue;
60221
- const ageMs = Date.now() - (0, import_node_fs24.statSync)(p).mtimeMs;
60222
- (0, import_node_fs24.rmSync)(p, { force: true });
60336
+ if (!(0, import_node_fs25.existsSync)(p)) continue;
60337
+ const ageMs = Date.now() - (0, import_node_fs25.statSync)(p).mtimeMs;
60338
+ (0, import_node_fs25.rmSync)(p, { force: true });
60223
60339
  if (ageMs < FRESH_MS) fresh = true;
60224
60340
  } catch {
60225
60341
  }
@@ -60236,13 +60352,13 @@ function currentVersion() {
60236
60352
  return appVersion();
60237
60353
  }
60238
60354
  function installRoot() {
60239
- return (0, import_node_path22.dirname)((0, import_node_path22.dirname)(process.execPath));
60355
+ return (0, import_node_path23.dirname)((0, import_node_path23.dirname)(process.execPath));
60240
60356
  }
60241
60357
  function updateExePath() {
60242
- return (0, import_node_path22.join)(installRoot(), "Update.exe");
60358
+ return (0, import_node_path23.join)(installRoot(), "Update.exe");
60243
60359
  }
60244
60360
  function packagesDir() {
60245
- return (0, import_node_path22.join)(installRoot(), "packages");
60361
+ return (0, import_node_path23.join)(installRoot(), "packages");
60246
60362
  }
60247
60363
  function feedUrl() {
60248
60364
  const env2 = (process.env.FLOLESS_UPDATE_URL ?? "").trim().replace(/\/+$/, "");
@@ -60335,22 +60451,22 @@ async function checkForUpdate() {
60335
60451
  }
60336
60452
  async function sha1OfFile(path) {
60337
60453
  const hash = (0, import_node_crypto6.createHash)("sha1");
60338
- await (0, import_promises.pipeline)((0, import_node_fs25.createReadStream)(path), hash);
60454
+ await (0, import_promises.pipeline)((0, import_node_fs26.createReadStream)(path), hash);
60339
60455
  return hash.digest("hex").toUpperCase();
60340
60456
  }
60341
60457
  async function downloadPackage(asset) {
60342
60458
  if (!NUPKG_NAME.test(asset.FileName)) throw new Error(`refusing suspicious package name: ${asset.FileName}`);
60343
60459
  const want = asset.SHA1.toUpperCase();
60344
60460
  const dir = packagesDir();
60345
- (0, import_node_fs25.mkdirSync)(dir, { recursive: true });
60346
- const dest = (0, import_node_path22.join)(dir, asset.FileName);
60347
- if ((0, import_node_fs25.existsSync)(dest) && await sha1OfFile(dest) === want) return dest;
60461
+ (0, import_node_fs26.mkdirSync)(dir, { recursive: true });
60462
+ const dest = (0, import_node_path23.join)(dir, asset.FileName);
60463
+ if ((0, import_node_fs26.existsSync)(dest) && await sha1OfFile(dest) === want) return dest;
60348
60464
  const res = await authedFetch(`${feedUrl()}/${encodeURIComponent(asset.FileName)}`, {
60349
60465
  redirect: "follow",
60350
60466
  signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS)
60351
60467
  });
60352
60468
  if (!res.ok || !res.body) throw new Error(`download failed: HTTP ${res.status} for ${asset.FileName}`);
60353
- await (0, import_promises.pipeline)(import_node_stream3.Readable.fromWeb(res.body), (0, import_node_fs25.createWriteStream)(dest));
60469
+ await (0, import_promises.pipeline)(import_node_stream3.Readable.fromWeb(res.body), (0, import_node_fs26.createWriteStream)(dest));
60354
60470
  const got = await sha1OfFile(dest);
60355
60471
  if (got !== want) throw new Error(`SHA1 mismatch for ${asset.FileName}: feed=${want} got=${got}`);
60356
60472
  return dest;
@@ -60359,7 +60475,7 @@ async function applyUpdate(check, opts) {
60359
60475
  if (!check.supported) return { applied: false, message: check.reason ?? "auto-update not supported in this runtime" };
60360
60476
  if (!check.updateAvailable || !check.asset) return { applied: false, message: check.reason ?? "no update available" };
60361
60477
  const exe = updateExePath();
60362
- if (!(0, import_node_fs25.existsSync)(exe)) {
60478
+ if (!(0, import_node_fs26.existsSync)(exe)) {
60363
60479
  return { applied: false, message: `Update.exe not found at ${exe} \u2014 is this a Velopack install?` };
60364
60480
  }
60365
60481
  const pkg = await downloadPackage(check.asset);
@@ -60599,12 +60715,12 @@ function isTraceCorrupt(events) {
60599
60715
 
60600
60716
  // launch.mjs
60601
60717
  var import_node_child_process6 = require("node:child_process");
60602
- var import_node_path23 = require("node:path");
60718
+ var import_node_path24 = require("node:path");
60603
60719
  var import_node_url3 = require("node:url");
60604
- var import_node_fs26 = require("node:fs");
60720
+ var import_node_fs27 = require("node:fs");
60605
60721
  var import_node_http = __toESM(require("node:http"), 1);
60606
60722
  var import_node_readline = require("node:readline");
60607
- var __dirname2 = (0, import_node_path23.dirname)((0, import_node_url3.fileURLToPath)(__import_meta_url));
60723
+ var __dirname2 = (0, import_node_path24.dirname)((0, import_node_url3.fileURLToPath)(__import_meta_url));
60608
60724
  var PORT = Number(process.env.PORT ?? 4317);
60609
60725
  var HEALTH_URL = `http://127.0.0.1:${PORT}/api/health`;
60610
60726
  var BROWSER_URL = `http://floless.localhost:${PORT}`;
@@ -60676,8 +60792,8 @@ async function waitHealthy(timeoutMs = 3e4) {
60676
60792
  function resolveServerStart() {
60677
60793
  const packaged = /flolessapp\.exe$/i.test(process.execPath);
60678
60794
  if (packaged) return { cmd: process.execPath, args: ["--serve"], shell: false };
60679
- const bundle = (0, import_node_path23.join)(__dirname2, "dist", "floless-server.cjs");
60680
- if ((0, import_node_fs26.existsSync)(bundle)) return { cmd: process.execPath, args: [bundle, "--serve"], shell: false };
60795
+ const bundle = (0, import_node_path24.join)(__dirname2, "dist", "floless-server.cjs");
60796
+ if ((0, import_node_fs27.existsSync)(bundle)) return { cmd: process.execPath, args: [bundle, "--serve"], shell: false };
60681
60797
  return { cmd: "npm", args: ["run", "start"], shell: isWin2 };
60682
60798
  }
60683
60799
  function startServerDetached() {
@@ -60830,8 +60946,8 @@ function taskkillArgs(pid, { tree = true } = {}) {
60830
60946
  }
60831
60947
  function killSupervisor({ tree = true } = {}) {
60832
60948
  if (!isWin2) return;
60833
- const isNpmChannel = /^node(\.exe)?$/i.test((0, import_node_path23.basename)(process.execPath));
60834
- const scriptMatch = isNpmChannel ? (0, import_node_path23.basename)((0, import_node_url3.fileURLToPath)(__import_meta_url)) : void 0;
60949
+ const isNpmChannel = /^node(\.exe)?$/i.test((0, import_node_path24.basename)(process.execPath));
60950
+ const scriptMatch = isNpmChannel ? (0, import_node_path24.basename)((0, import_node_url3.fileURLToPath)(__import_meta_url)) : void 0;
60835
60951
  const realExe = resolveRealInstallExe(process.execPath);
60836
60952
  const exeMatch = realExe === process.execPath ? process.execPath : [process.execPath, realExe];
60837
60953
  const pids = supervisorPidsToKill(enumerateProcesses(), process.pid, exeMatch, scriptMatch);
@@ -60996,7 +61112,7 @@ async function runAction(arg, flagArgv = [], selfVersion = null) {
60996
61112
  }
60997
61113
  await action(parseTeardownFlags(flagArgv));
60998
61114
  }
60999
- var entry = (0, import_node_path23.basename)(process.argv[1] ?? "").toLowerCase();
61115
+ var entry = (0, import_node_path24.basename)(process.argv[1] ?? "").toLowerCase();
61000
61116
  if (entry === "launch.mjs") {
61001
61117
  runAction(process.argv[2], process.argv.slice(3)).catch((e) => {
61002
61118
  log(`error: ${e?.message ?? e}`);
@@ -61070,9 +61186,9 @@ function awareUpgradeBlockReason(s) {
61070
61186
  }
61071
61187
 
61072
61188
  // skill-sync.ts
61073
- var import_node_fs27 = require("node:fs");
61074
- var import_node_os17 = require("node:os");
61075
- var import_node_path24 = require("node:path");
61189
+ var import_node_fs28 = require("node:fs");
61190
+ var import_node_os18 = require("node:os");
61191
+ var import_node_path25 = require("node:path");
61076
61192
  var import_node_url4 = require("node:url");
61077
61193
  var import_yaml8 = __toESM(require_dist6(), 1);
61078
61194
 
@@ -61113,30 +61229,30 @@ function selectShippedSkillNames(names) {
61113
61229
  }
61114
61230
 
61115
61231
  // skill-sync.ts
61116
- var __dirname3 = (0, import_node_path24.dirname)((0, import_node_url4.fileURLToPath)(__import_meta_url));
61232
+ var __dirname3 = (0, import_node_path25.dirname)((0, import_node_url4.fileURLToPath)(__import_meta_url));
61117
61233
  function bundledSkillsRoot() {
61118
61234
  const candidates = [
61119
- (0, import_node_path24.join)(__dirname3, "skills"),
61120
- (0, import_node_path24.join)((0, import_node_path24.dirname)(process.execPath), "skills"),
61121
- (0, import_node_path24.join)(__dirname3, "..", ".claude", "skills")
61235
+ (0, import_node_path25.join)(__dirname3, "skills"),
61236
+ (0, import_node_path25.join)((0, import_node_path25.dirname)(process.execPath), "skills"),
61237
+ (0, import_node_path25.join)(__dirname3, "..", ".claude", "skills")
61122
61238
  ];
61123
- return candidates.find((p) => (0, import_node_fs27.existsSync)(p)) ?? null;
61239
+ return candidates.find((p) => (0, import_node_fs28.existsSync)(p)) ?? null;
61124
61240
  }
61125
61241
  function targetConfigDirs() {
61126
61242
  const override = process.env.FLOLESS_SKILL_TARGETS;
61127
61243
  if (override) {
61128
61244
  return override.split(";").map((d) => d.trim()).filter(Boolean).map((dir) => ({ runtime: "custom", dir }));
61129
61245
  }
61130
- const home = (0, import_node_os17.homedir)();
61246
+ const home = (0, import_node_os18.homedir)();
61131
61247
  return [
61132
- { runtime: "claude", dir: (0, import_node_path24.join)(home, ".claude") },
61133
- { runtime: "codex", dir: (0, import_node_path24.join)(home, ".codex") },
61134
- { runtime: "opencode", dir: (0, import_node_path24.join)(home, ".opencode") }
61248
+ { runtime: "claude", dir: (0, import_node_path25.join)(home, ".claude") },
61249
+ { runtime: "codex", dir: (0, import_node_path25.join)(home, ".codex") },
61250
+ { runtime: "opencode", dir: (0, import_node_path25.join)(home, ".opencode") }
61135
61251
  ];
61136
61252
  }
61137
61253
  function skillVersion(skillMdPath) {
61138
61254
  try {
61139
- const text = (0, import_node_fs27.readFileSync)(skillMdPath, "utf8");
61255
+ const text = (0, import_node_fs28.readFileSync)(skillMdPath, "utf8");
61140
61256
  const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(text);
61141
61257
  if (!m || m[1] === void 0) return null;
61142
61258
  const fm = (0, import_yaml8.parse)(m[1]);
@@ -61176,21 +61292,21 @@ function decideAction(installed, bundled) {
61176
61292
  function bundledSkills(root) {
61177
61293
  let entries = [];
61178
61294
  try {
61179
- entries = selectShippedSkillNames((0, import_node_fs27.readdirSync)(root));
61295
+ entries = selectShippedSkillNames((0, import_node_fs28.readdirSync)(root));
61180
61296
  } catch {
61181
61297
  return [];
61182
61298
  }
61183
61299
  const out = [];
61184
61300
  for (const name of entries) {
61185
- const dir = (0, import_node_path24.join)(root, name);
61301
+ const dir = (0, import_node_path25.join)(root, name);
61186
61302
  let isDir = false;
61187
61303
  try {
61188
- isDir = (0, import_node_fs27.statSync)(dir).isDirectory();
61304
+ isDir = (0, import_node_fs28.statSync)(dir).isDirectory();
61189
61305
  } catch {
61190
61306
  isDir = false;
61191
61307
  }
61192
61308
  if (!isDir) continue;
61193
- const v = skillVersion((0, import_node_path24.join)(dir, "SKILL.md"));
61309
+ const v = skillVersion((0, import_node_path25.join)(dir, "SKILL.md"));
61194
61310
  if (!v) continue;
61195
61311
  out.push({ name, dir, version: v });
61196
61312
  }
@@ -61203,17 +61319,17 @@ function syncSkills() {
61203
61319
  const skills = bundledSkills(root);
61204
61320
  if (!skills.length) return results;
61205
61321
  for (const { runtime, dir: cfg } of targetConfigDirs()) {
61206
- if (!(0, import_node_fs27.existsSync)(cfg)) continue;
61207
- const skillsDir = (0, import_node_path24.join)(cfg, "skills");
61322
+ if (!(0, import_node_fs28.existsSync)(cfg)) continue;
61323
+ const skillsDir = (0, import_node_path25.join)(cfg, "skills");
61208
61324
  for (const s of skills) {
61209
- const dest = (0, import_node_path24.join)(skillsDir, s.name);
61210
- const installedMd = (0, import_node_path24.join)(dest, "SKILL.md");
61211
- const installed = (0, import_node_fs27.existsSync)(installedMd) ? skillVersion(installedMd) : null;
61325
+ const dest = (0, import_node_path25.join)(skillsDir, s.name);
61326
+ const installedMd = (0, import_node_path25.join)(dest, "SKILL.md");
61327
+ const installed = (0, import_node_fs28.existsSync)(installedMd) ? skillVersion(installedMd) : null;
61212
61328
  const action = decideAction(installed, s.version);
61213
61329
  if (action === "installed" || action === "updated") {
61214
61330
  try {
61215
- if (action === "updated") (0, import_node_fs27.rmSync)(dest, { recursive: true, force: true });
61216
- (0, import_node_fs27.cpSync)(s.dir, dest, { recursive: true });
61331
+ if (action === "updated") (0, import_node_fs28.rmSync)(dest, { recursive: true, force: true });
61332
+ (0, import_node_fs28.cpSync)(s.dir, dest, { recursive: true });
61217
61333
  results.push({ runtime, skill: s.name, action, from: installed, to: s.version });
61218
61334
  } catch {
61219
61335
  }
@@ -61226,9 +61342,9 @@ function syncSkills() {
61226
61342
  }
61227
61343
 
61228
61344
  // watch.ts
61229
- var import_node_os18 = require("node:os");
61230
- var import_node_path26 = require("node:path");
61231
- var import_node_fs28 = require("node:fs");
61345
+ var import_node_os19 = require("node:os");
61346
+ var import_node_path27 = require("node:path");
61347
+ var import_node_fs29 = require("node:fs");
61232
61348
 
61233
61349
  // node_modules/chokidar/esm/index.js
61234
61350
  var import_fs2 = require("fs");
@@ -61239,7 +61355,7 @@ var sysPath2 = __toESM(require("path"), 1);
61239
61355
  // node_modules/readdirp/esm/index.js
61240
61356
  var import_promises2 = require("node:fs/promises");
61241
61357
  var import_node_stream4 = require("node:stream");
61242
- var import_node_path25 = require("node:path");
61358
+ var import_node_path26 = require("node:path");
61243
61359
  var EntryTypes = {
61244
61360
  FILE_TYPE: "files",
61245
61361
  DIR_TYPE: "directories",
@@ -61314,7 +61430,7 @@ var ReaddirpStream = class extends import_node_stream4.Readable {
61314
61430
  this._wantsDir = type ? DIR_TYPES.has(type) : false;
61315
61431
  this._wantsFile = type ? FILE_TYPES.has(type) : false;
61316
61432
  this._wantsEverything = type === EntryTypes.EVERYTHING_TYPE;
61317
- this._root = (0, import_node_path25.resolve)(root);
61433
+ this._root = (0, import_node_path26.resolve)(root);
61318
61434
  this._isDirent = !opts.alwaysStat;
61319
61435
  this._statsProp = this._isDirent ? "dirent" : "stats";
61320
61436
  this._rdOptions = { encoding: "utf8", withFileTypes: this._isDirent };
@@ -61385,8 +61501,8 @@ var ReaddirpStream = class extends import_node_stream4.Readable {
61385
61501
  let entry2;
61386
61502
  const basename5 = this._isDirent ? dirent.name : dirent;
61387
61503
  try {
61388
- const fullPath = (0, import_node_path25.resolve)((0, import_node_path25.join)(path, basename5));
61389
- entry2 = { path: (0, import_node_path25.relative)(this._root, fullPath), fullPath, basename: basename5 };
61504
+ const fullPath = (0, import_node_path26.resolve)((0, import_node_path26.join)(path, basename5));
61505
+ entry2 = { path: (0, import_node_path26.relative)(this._root, fullPath), fullPath, basename: basename5 };
61390
61506
  entry2[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
61391
61507
  } catch (err2) {
61392
61508
  this._onError(err2);
@@ -61420,7 +61536,7 @@ var ReaddirpStream = class extends import_node_stream4.Readable {
61420
61536
  }
61421
61537
  if (entryRealPathStats.isDirectory()) {
61422
61538
  const len2 = entryRealPath.length;
61423
- if (full.startsWith(entryRealPath) && full.substr(len2, 1) === import_node_path25.sep) {
61539
+ if (full.startsWith(entryRealPath) && full.substr(len2, 1) === import_node_path26.sep) {
61424
61540
  const recursiveError = new Error(`Circular symlink detected: "${full}" points to "${entryRealPath}"`);
61425
61541
  recursiveError.code = RECURSIVE_ERROR_CODE;
61426
61542
  return this._onError(recursiveError);
@@ -61959,9 +62075,9 @@ var NodeFsHandler = class {
61959
62075
  if (this.fsw.closed) {
61960
62076
  return;
61961
62077
  }
61962
- const dirname12 = sysPath.dirname(file);
62078
+ const dirname13 = sysPath.dirname(file);
61963
62079
  const basename5 = sysPath.basename(file);
61964
- const parent = this.fsw._getWatchedDir(dirname12);
62080
+ const parent = this.fsw._getWatchedDir(dirname13);
61965
62081
  let prevStats = stats;
61966
62082
  if (parent.has(basename5))
61967
62083
  return;
@@ -61988,7 +62104,7 @@ var NodeFsHandler = class {
61988
62104
  prevStats = newStats2;
61989
62105
  }
61990
62106
  } catch (error) {
61991
- this.fsw._remove(dirname12, basename5);
62107
+ this.fsw._remove(dirname13, basename5);
61992
62108
  }
61993
62109
  } else if (parent.has(basename5)) {
61994
62110
  const at = newStats.atimeMs;
@@ -62928,33 +63044,33 @@ function appIdFromLogPath(path) {
62928
63044
  return i >= 0 && parts[i + 1] ? parts[i + 1] : null;
62929
63045
  }
62930
63046
  function samePath(a, b) {
62931
- const ra = (0, import_node_path26.resolve)(a);
62932
- const rb = (0, import_node_path26.resolve)(b);
63047
+ const ra = (0, import_node_path27.resolve)(a);
63048
+ const rb = (0, import_node_path27.resolve)(b);
62933
63049
  return process.platform === "win32" ? ra.toLowerCase() === rb.toLowerCase() : ra === rb;
62934
63050
  }
62935
63051
  function underDir(path, dir) {
62936
- const rp = (0, import_node_path26.resolve)(path);
62937
- const rd = (0, import_node_path26.resolve)(dir);
63052
+ const rp = (0, import_node_path27.resolve)(path);
63053
+ const rd = (0, import_node_path27.resolve)(dir);
62938
63054
  const [p, d] = process.platform === "win32" ? [rp.toLowerCase(), rd.toLowerCase()] : [rp, rd];
62939
- return p === d || p.startsWith(d + import_node_path26.sep);
63055
+ return p === d || p.startsWith(d + import_node_path27.sep);
62940
63056
  }
62941
63057
  function startWatcher() {
62942
- const awareDir = process.env.AWARE_HOME ?? (0, import_node_path26.join)((0, import_node_os18.homedir)(), ".aware");
62943
- const credentialsDir = (0, import_node_path26.join)(awareDir, "credentials");
62944
- if (!(0, import_node_fs28.existsSync)(credentialsDir)) {
63058
+ const awareDir = process.env.AWARE_HOME ?? (0, import_node_path27.join)((0, import_node_os19.homedir)(), ".aware");
63059
+ const credentialsDir = (0, import_node_path27.join)(awareDir, "credentials");
63060
+ if (!(0, import_node_fs29.existsSync)(credentialsDir)) {
62945
63061
  try {
62946
- (0, import_node_fs28.mkdirSync)(credentialsDir, { recursive: true });
63062
+ (0, import_node_fs29.mkdirSync)(credentialsDir, { recursive: true });
62947
63063
  } catch {
62948
63064
  }
62949
63065
  }
62950
- if (!(0, import_node_fs28.existsSync)(uiDir)) {
63066
+ if (!(0, import_node_fs29.existsSync)(uiDir)) {
62951
63067
  try {
62952
- (0, import_node_fs28.mkdirSync)(uiDir, { recursive: true });
63068
+ (0, import_node_fs29.mkdirSync)(uiDir, { recursive: true });
62953
63069
  } catch {
62954
63070
  }
62955
63071
  }
62956
- const targets = ["apps", "logs", "credentials"].map((d) => (0, import_node_path26.join)(awareDir, d)).filter((p) => (0, import_node_fs28.existsSync)(p));
62957
- if ((0, import_node_fs28.existsSync)(uiDir)) targets.push(uiDir);
63072
+ const targets = ["apps", "logs", "credentials"].map((d) => (0, import_node_path27.join)(awareDir, d)).filter((p) => (0, import_node_fs29.existsSync)(p));
63073
+ if ((0, import_node_fs29.existsSync)(uiDir)) targets.push(uiDir);
62958
63074
  if (targets.length === 0) {
62959
63075
  return null;
62960
63076
  }
@@ -62983,11 +63099,11 @@ function startWatcher() {
62983
63099
  const isCredential = path.split(/[\\/]/).includes("credentials");
62984
63100
  const kind = isCredential ? "credential" : path.endsWith(".jsonl") ? "trace" : path.endsWith(".lock") ? "lock" : path.endsWith(".flo") || path.endsWith(".app") ? "source" : "file";
62985
63101
  broadcast({ type: "fs-change", kind, event, path });
62986
- if (kind === "trace" && event !== "unlink" && (0, import_node_fs28.existsSync)(path)) {
63102
+ if (kind === "trace" && event !== "unlink" && (0, import_node_fs29.existsSync)(path)) {
62987
63103
  const id = appIdFromLogPath(path);
62988
63104
  if (!id) return;
62989
63105
  try {
62990
- broadcast({ type: "trace-file", id, runId: path.split(import_node_path26.sep).pop()?.replace(/\.jsonl$/, "") ?? null, events: parseTrace((0, import_node_fs28.readFileSync)(path, "utf8")) });
63106
+ broadcast({ type: "trace-file", id, runId: path.split(import_node_path27.sep).pop()?.replace(/\.jsonl$/, "") ?? null, events: parseTrace((0, import_node_fs29.readFileSync)(path, "utf8")) });
62991
63107
  } catch {
62992
63108
  }
62993
63109
  }
@@ -62996,10 +63112,10 @@ function startWatcher() {
62996
63112
  }
62997
63113
 
62998
63114
  // index.ts
62999
- var __dirname4 = (0, import_node_path27.dirname)((0, import_node_url5.fileURLToPath)(__import_meta_url));
63000
- var WEB_ROOT = [(0, import_node_path27.join)(__dirname4, "web"), (0, import_node_path27.join)((0, import_node_path27.dirname)(process.execPath), "web"), (0, import_node_path27.join)(__dirname4, "..", "web")].find(
63001
- (p) => (0, import_node_fs29.existsSync)(p)
63002
- ) ?? (0, import_node_path27.join)(__dirname4, "..", "web");
63115
+ var __dirname4 = (0, import_node_path28.dirname)((0, import_node_url5.fileURLToPath)(__import_meta_url));
63116
+ var WEB_ROOT = [(0, import_node_path28.join)(__dirname4, "web"), (0, import_node_path28.join)((0, import_node_path28.dirname)(process.execPath), "web"), (0, import_node_path28.join)(__dirname4, "..", "web")].find(
63117
+ (p) => (0, import_node_fs30.existsSync)(p)
63118
+ ) ?? (0, import_node_path28.join)(__dirname4, "..", "web");
63003
63119
  var PORT2 = Number(process.env.PORT ?? 4317);
63004
63120
  var HOST = "127.0.0.1";
63005
63121
  var crashHandlersInstalled = false;
@@ -63015,7 +63131,7 @@ function installCrashHandlers() {
63015
63131
  ${stack}
63016
63132
  `;
63017
63133
  try {
63018
- (0, import_node_fs29.appendFileSync)(logFilePath(), line);
63134
+ (0, import_node_fs30.appendFileSync)(logFilePath(), line);
63019
63135
  } catch {
63020
63136
  }
63021
63137
  if (process.stderr.isTTY) process.stderr.write(line);
@@ -63278,24 +63394,64 @@ async function startServer() {
63278
63394
  });
63279
63395
  app.get("/api/apps", async () => {
63280
63396
  const apps = await aware.list();
63281
- return { ok: true, apps: apps.map((a) => ({ ...a, provider: appProvider(a.id), baked: appBaked(a.id) })) };
63397
+ return { ok: true, apps: apps.map((a) => ({ ...a, provider: appProvider(a.id), baked: appBaked(a.id), ...appOrigin(a.id) })) };
63282
63398
  });
63283
63399
  app.get("/api/workflows/updates", async () => {
63284
63400
  const apps = await aware.list();
63285
- const installed = {};
63401
+ const installed = [];
63286
63402
  for (const a of apps) {
63287
63403
  try {
63288
- installed[a.id] = readApp(a.id).version;
63404
+ const { templateId, forked } = appOrigin(a.id);
63405
+ const app2 = readApp(a.id);
63406
+ installed.push({ id: a.id, version: app2.version, templateId, forked, publisher: app2.publisher });
63289
63407
  } catch {
63290
63408
  }
63291
63409
  }
63292
63410
  return { ok: true, updates: detectUpdates(installed, loadTemplates()) };
63293
63411
  });
63294
63412
  const _wfUpdating = /* @__PURE__ */ new Set();
63413
+ class TemplateSwapError extends Error {
63414
+ code;
63415
+ backupPath;
63416
+ constructor(message, code, backupPath) {
63417
+ super(message);
63418
+ this.name = "TemplateSwapError";
63419
+ this.code = code;
63420
+ this.backupPath = backupPath;
63421
+ }
63422
+ }
63423
+ async function swapTemplateIntoApp(opts) {
63424
+ const { id, templateId, templatePath, srcPath, appDirPath: appDirPath2, fromVersion, contract, verb } = opts;
63425
+ const bdir = backupDir(id, fromVersion, Date.now());
63426
+ try {
63427
+ (0, import_node_fs30.mkdirSync)((0, import_node_path28.dirname)(bdir), { recursive: true });
63428
+ (0, import_node_fs30.cpSync)(appDirPath2, bdir, { recursive: true });
63429
+ (0, import_node_fs30.cpSync)(templatePath, srcPath);
63430
+ if (id !== templateId) stampSourceAppId(srcPath, id);
63431
+ if (contract != null) {
63432
+ bakeContractIntoApp(srcPath, contract);
63433
+ await bakeDownstream(srcPath, id, contract);
63434
+ }
63435
+ await aware.compile(srcPath);
63436
+ return { backupPath: bdir };
63437
+ } catch (e) {
63438
+ const msg = e instanceof Error ? e.message : String(e);
63439
+ try {
63440
+ (0, import_node_fs30.rmSync)(appDirPath2, { recursive: true, force: true });
63441
+ (0, import_node_fs30.cpSync)(bdir, appDirPath2, { recursive: true });
63442
+ } catch (re) {
63443
+ const rmsg = re instanceof Error ? re.message : String(re);
63444
+ throw new TemplateSwapError(`${verb} failed (${msg}) AND rollback failed (${rmsg}) \u2014 restore manually from ${bdir}`, "rollback-failed", bdir);
63445
+ }
63446
+ throw new TemplateSwapError(`${verb} failed (rolled back to ${fromVersion}): ${msg}`, `${verb}-failed`);
63447
+ }
63448
+ }
63295
63449
  app.post("/api/workflows/:id/update", async (req, reply) => {
63296
63450
  const id = req.params.id;
63297
- const t = loadTemplates().find((x) => x.id === id);
63298
- if (!t) return reply.status(404).send({ ok: false, error: `no bundled template for "${id}"`, code: "no-template" });
63451
+ const { templateId, forked } = appOrigin(id);
63452
+ if (forked) return reply.status(409).send({ ok: false, error: `"${id}" is a fork (off the update channel) \u2014 restore it to the official version first to rejoin`, code: "forked" });
63453
+ const t = loadTemplates().find((x) => x.id === templateId);
63454
+ if (!t) return reply.status(404).send({ ok: false, error: `no bundled template in the lineage of "${id}"`, code: "no-template" });
63299
63455
  if (t.publisher !== "floless") return reply.status(400).send({ ok: false, error: `"${id}" is not a FloLess-published workflow`, code: "not-published" });
63300
63456
  if (_wfUpdating.has(id)) return reply.status(409).send({ ok: false, error: `an update for "${id}" is already in progress`, code: "in-progress" });
63301
63457
  let fromVersion;
@@ -63313,7 +63469,7 @@ async function startServer() {
63313
63469
  return reply.status(409).send({ ok: false, error: `"${id}" is already at ${fromVersion}`, code: "up-to-date" });
63314
63470
  }
63315
63471
  const contract = readContract(id);
63316
- if (contract == null && (0, import_node_fs29.existsSync)(contractPath(id))) {
63472
+ if (contract == null && (0, import_node_fs30.existsSync)(contractPath(id))) {
63317
63473
  return reply.status(409).send({ ok: false, error: `"${id}"'s saved data is unreadable \u2014 open it in the editor and re-save before updating`, code: "contract-corrupt" });
63318
63474
  }
63319
63475
  if (contract != null) {
@@ -63321,28 +63477,13 @@ async function startServer() {
63321
63477
  if (!v.valid) return reply.status(409).send({ ok: false, error: `"${id}"'s saved data is invalid \u2014 re-save it in the editor before updating`, code: "contract-invalid" });
63322
63478
  }
63323
63479
  _wfUpdating.add(id);
63324
- const bdir = backupDir(id, fromVersion, Date.now());
63325
63480
  try {
63326
- (0, import_node_fs29.mkdirSync)((0, import_node_path27.dirname)(bdir), { recursive: true });
63327
- (0, import_node_fs29.cpSync)(appDirPath2, bdir, { recursive: true });
63328
- (0, import_node_fs29.cpSync)(t.path, srcPath);
63329
- if (contract != null) {
63330
- bakeContractIntoApp(srcPath, contract);
63331
- await bakeDownstream(srcPath, id, contract);
63332
- }
63333
- await aware.compile(srcPath);
63481
+ const { backupPath } = await swapTemplateIntoApp({ id, templateId, templatePath: t.path, srcPath, appDirPath: appDirPath2, fromVersion, contract, verb: "update" });
63334
63482
  broadcast({ type: "apps-changed", id });
63335
- return { ok: true, id, fromVersion, toVersion: t.version, backupPath: bdir };
63483
+ return { ok: true, id, fromVersion, toVersion: t.version, backupPath };
63336
63484
  } catch (e) {
63337
- const msg = e instanceof Error ? e.message : String(e);
63338
- try {
63339
- (0, import_node_fs29.rmSync)(appDirPath2, { recursive: true, force: true });
63340
- (0, import_node_fs29.cpSync)(bdir, appDirPath2, { recursive: true });
63341
- } catch (re) {
63342
- const rmsg = re instanceof Error ? re.message : String(re);
63343
- return reply.status(500).send({ ok: false, error: `update failed (${msg}) AND rollback failed (${rmsg}) \u2014 restore manually from ${bdir}`, code: "rollback-failed", backupPath: bdir });
63344
- }
63345
- return reply.status(500).send({ ok: false, error: `update failed (rolled back to ${fromVersion}): ${msg}`, code: "update-failed" });
63485
+ if (e instanceof TemplateSwapError) return reply.status(500).send({ ok: false, error: e.message, code: e.code, ...e.backupPath ? { backupPath: e.backupPath } : {} });
63486
+ throw e;
63346
63487
  } finally {
63347
63488
  _wfUpdating.delete(id);
63348
63489
  }
@@ -63609,7 +63750,7 @@ async function startServer() {
63609
63750
  }
63610
63751
  const companionId = `${req.params.appId}-ifc`;
63611
63752
  const filename = `${req.params.appId}.ifc`;
63612
- const outPath = (0, import_node_path27.join)(appPath(companionId), filename);
63753
+ const outPath = (0, import_node_path28.join)(appPath(companionId), filename);
63613
63754
  let flo;
63614
63755
  try {
63615
63756
  flo = writeIfcApp(appPath(companionId), companionId, scene, outPath);
@@ -63632,8 +63773,8 @@ async function startServer() {
63632
63773
  }
63633
63774
  throw e;
63634
63775
  }
63635
- if (!(0, import_node_fs29.existsSync)(outPath)) return reply.send({ ok: false, error: "the IFC export produced no file" });
63636
- const content = (0, import_node_fs29.readFileSync)(outPath, "utf8");
63776
+ if (!(0, import_node_fs30.existsSync)(outPath)) return reply.send({ ok: false, error: "the IFC export produced no file" });
63777
+ const content = (0, import_node_fs30.readFileSync)(outPath, "utf8");
63637
63778
  broadcast({ type: "apps-changed" });
63638
63779
  return { ok: true, filename, content, bytes: Buffer.byteLength(content), skipped };
63639
63780
  });
@@ -63712,17 +63853,17 @@ async function startServer() {
63712
63853
  return reply.status(422).send({ ok: false, error: "no priced members to export \u2014 every member is an RFI (no AISC size / weight yet)" });
63713
63854
  }
63714
63855
  const outPath = bomExportPath(appId, format);
63715
- const dir = (0, import_node_path27.dirname)(outPath);
63856
+ const dir = (0, import_node_path28.dirname)(outPath);
63716
63857
  const filename = `${appId}-bom.${format}`;
63717
63858
  try {
63718
- (0, import_node_fs29.mkdirSync)(dir, { recursive: true });
63859
+ (0, import_node_fs30.mkdirSync)(dir, { recursive: true });
63719
63860
  if (format === "csv") {
63720
63861
  const text = bomToCsv(bom);
63721
- (0, import_node_fs29.writeFileSync)(outPath, text, "utf8");
63862
+ (0, import_node_fs30.writeFileSync)(outPath, text, "utf8");
63722
63863
  return { ok: true, filename, encoding: "utf8", content: text, bytes: Buffer.byteLength(text), savedTo: outPath };
63723
63864
  }
63724
63865
  const buf = await bomToXlsx(bom);
63725
- (0, import_node_fs29.writeFileSync)(outPath, buf);
63866
+ (0, import_node_fs30.writeFileSync)(outPath, buf);
63726
63867
  return { ok: true, filename, encoding: "base64", content: buf.toString("base64"), bytes: buf.length, savedTo: outPath };
63727
63868
  } catch (e) {
63728
63869
  app.log.error({ appId, format, err: e instanceof Error ? e.message : e }, "export-bom: generation failed");
@@ -63758,6 +63899,60 @@ async function startServer() {
63758
63899
  broadcast({ type: "apps-changed", id: req.params.id });
63759
63900
  return { ok: true };
63760
63901
  });
63902
+ app.post("/api/app/:id/fork", async (req, reply) => {
63903
+ const id = req.params.id;
63904
+ if (!appExists(id)) return reply.status(404).send({ ok: false, error: `"${id}" is not installed`, code: "not-installed" });
63905
+ if (req.body?.accept !== true) return reply.status(400).send({ ok: false, error: "liability terms not accepted", code: "terms-required" });
63906
+ let version;
63907
+ try {
63908
+ version = readApp(id).version;
63909
+ } catch {
63910
+ return reply.status(404).send({ ok: false, error: `"${id}" is not installed`, code: "not-installed" });
63911
+ }
63912
+ const acceptedBy = typeof req.body?.acceptedBy === "string" ? req.body.acceptedBy : void 0;
63913
+ markForked(id, { version, acceptedBy }, (/* @__PURE__ */ new Date()).toISOString());
63914
+ broadcast({ type: "apps-changed", id });
63915
+ return { ok: true, id, forked: true };
63916
+ });
63917
+ app.post("/api/app/:id/restore", async (req, reply) => {
63918
+ const id = req.params.id;
63919
+ const { templateId } = appOrigin(id);
63920
+ const t = loadTemplates().find((x) => x.id === templateId);
63921
+ if (!t) return reply.status(404).send({ ok: false, error: `no bundled template to restore "${id}" from`, code: "no-template" });
63922
+ if (t.publisher !== "floless") return reply.status(400).send({ ok: false, error: `"${id}" has no FloLess-published template to restore from`, code: "not-published" });
63923
+ if (_wfUpdating.has(id)) return reply.status(409).send({ ok: false, error: `an operation for "${id}" is already in progress`, code: "in-progress" });
63924
+ let fromVersion;
63925
+ let srcPath;
63926
+ let appDirPath2;
63927
+ try {
63928
+ const a = readApp(id);
63929
+ fromVersion = a.version;
63930
+ srcPath = a.source.path;
63931
+ appDirPath2 = appDir(id);
63932
+ } catch {
63933
+ return reply.status(404).send({ ok: false, error: `"${id}" is not installed`, code: "not-installed" });
63934
+ }
63935
+ const contract = readContract(id);
63936
+ if (contract == null && (0, import_node_fs30.existsSync)(contractPath(id))) {
63937
+ return reply.status(409).send({ ok: false, error: `"${id}"'s saved data is unreadable \u2014 open it in the editor and re-save before restoring`, code: "contract-corrupt" });
63938
+ }
63939
+ if (contract != null) {
63940
+ const v = validateSteelTakeoff(contract);
63941
+ if (!v.valid) return reply.status(409).send({ ok: false, error: `"${id}"'s saved data is invalid \u2014 re-save it in the editor before restoring`, code: "contract-invalid" });
63942
+ }
63943
+ _wfUpdating.add(id);
63944
+ try {
63945
+ const { backupPath } = await swapTemplateIntoApp({ id, templateId, templatePath: t.path, srcPath, appDirPath: appDirPath2, fromVersion, contract, verb: "restore" });
63946
+ clearFork(id);
63947
+ broadcast({ type: "apps-changed", id });
63948
+ return { ok: true, id, restored: true, version: t.version, backupPath };
63949
+ } catch (e) {
63950
+ if (e instanceof TemplateSwapError) return reply.status(500).send({ ok: false, error: e.message, code: e.code, ...e.backupPath ? { backupPath: e.backupPath } : {} });
63951
+ throw e;
63952
+ } finally {
63953
+ _wfUpdating.delete(id);
63954
+ }
63955
+ });
63761
63956
  app.post("/api/app/:id/freeze", async (req, reply) => {
63762
63957
  const nodeId = typeof req.body?.nodeId === "string" ? req.body.nodeId.trim() : "";
63763
63958
  if (!nodeId) return reply.status(400).send({ ok: false, error: "nodeId required" });
@@ -63816,11 +64011,11 @@ async function startServer() {
63816
64011
  if (appExists(id)) {
63817
64012
  return reply.status(409).send({ ok: false, error: `a workflow named "${id}" is already installed \u2014 remove it first, or rename the file's app: id`, code: "exists", id });
63818
64013
  }
63819
- const stageRoot = (0, import_node_fs29.mkdtempSync)((0, import_node_path27.join)((0, import_node_os19.tmpdir)(), "floless-import-"));
64014
+ const stageRoot = (0, import_node_fs30.mkdtempSync)((0, import_node_path28.join)((0, import_node_os20.tmpdir)(), "floless-import-"));
63820
64015
  try {
63821
- const stageDir = (0, import_node_path27.join)(stageRoot, id);
63822
- (0, import_node_fs29.mkdirSync)(stageDir);
63823
- (0, import_node_fs29.writeFileSync)((0, import_node_path27.join)(stageDir, `${id}.flo`), content);
64016
+ const stageDir = (0, import_node_path28.join)(stageRoot, id);
64017
+ (0, import_node_fs30.mkdirSync)(stageDir);
64018
+ (0, import_node_fs30.writeFileSync)((0, import_node_path28.join)(stageDir, `${id}.flo`), content);
63824
64019
  await aware.install(stageDir);
63825
64020
  } catch (err2) {
63826
64021
  try {
@@ -63830,7 +64025,7 @@ async function startServer() {
63830
64025
  const msg = err2 instanceof AwareError ? err2.message : String(err2?.message ?? err2);
63831
64026
  return reply.status(502).send({ ok: false, error: `import failed: ${msg}` });
63832
64027
  } finally {
63833
- (0, import_node_fs29.rmSync)(stageRoot, { recursive: true, force: true });
64028
+ (0, import_node_fs30.rmSync)(stageRoot, { recursive: true, force: true });
63834
64029
  }
63835
64030
  broadcast({ type: "apps-changed", id });
63836
64031
  return { ok: true, id };
@@ -63844,13 +64039,13 @@ async function startServer() {
63844
64039
  }
63845
64040
  const inputs = appData.inputs.map((i) => ({ name: i.name, type: i.type }));
63846
64041
  const baked = bakeFloSource(appData.source.text, inputs);
63847
- const tmpRoot = (0, import_node_fs29.mkdtempSync)((0, import_node_path27.join)((0, import_node_os19.tmpdir)(), "floless-bake-"));
63848
- const backupDir2 = (0, import_node_path27.join)(tmpRoot, `${id}-backup`);
63849
- const bakeDir = (0, import_node_path27.join)(tmpRoot, id);
63850
- (0, import_node_fs29.cpSync)(appDir(id), backupDir2, { recursive: true });
63851
- (0, import_node_fs29.cpSync)(appDir(id), bakeDir, { recursive: true });
64042
+ const tmpRoot = (0, import_node_fs30.mkdtempSync)((0, import_node_path28.join)((0, import_node_os20.tmpdir)(), "floless-bake-"));
64043
+ const backupDir2 = (0, import_node_path28.join)(tmpRoot, `${id}-backup`);
64044
+ const bakeDir = (0, import_node_path28.join)(tmpRoot, id);
64045
+ (0, import_node_fs30.cpSync)(appDir(id), backupDir2, { recursive: true });
64046
+ (0, import_node_fs30.cpSync)(appDir(id), bakeDir, { recursive: true });
63852
64047
  const floName = appData.source.path.split(/[\\/]/).pop();
63853
- (0, import_node_fs29.writeFileSync)((0, import_node_path27.join)(bakeDir, floName), baked);
64048
+ (0, import_node_fs30.writeFileSync)((0, import_node_path28.join)(bakeDir, floName), baked);
63854
64049
  let appInstalled2 = true;
63855
64050
  try {
63856
64051
  await aware.uninstall(id);
@@ -63872,17 +64067,17 @@ async function startServer() {
63872
64067
  throw installErr;
63873
64068
  }
63874
64069
  try {
63875
- await aware.compile((0, import_node_path27.join)(appDir(id), floName));
64070
+ await aware.compile((0, import_node_path28.join)(appDir(id), floName));
63876
64071
  } catch (compileErr) {
63877
64072
  app.log.warn({ id, compileErr: String(compileErr) }, "bake: post-install recompile failed (app baked but may need a manual Compile)");
63878
64073
  }
63879
64074
  broadcast({ type: "baked", id });
63880
64075
  return { ok: true, id, agent: id, inputs };
63881
64076
  } finally {
63882
- if (appInstalled2) (0, import_node_fs29.rmSync)(tmpRoot, { recursive: true, force: true });
64077
+ if (appInstalled2) (0, import_node_fs30.rmSync)(tmpRoot, { recursive: true, force: true });
63883
64078
  }
63884
64079
  });
63885
- const graftAgentsDir = () => (0, import_node_path27.join)((0, import_node_os19.homedir)(), ".aware", "agents");
64080
+ const graftAgentsDir = () => (0, import_node_path28.join)((0, import_node_os20.homedir)(), ".aware", "agents");
63886
64081
  app.post("/api/graft/match", async (req, reply) => {
63887
64082
  const { glob } = req.body ?? {};
63888
64083
  if (!glob) return reply.status(400).send({ ok: false, error: "glob required" });
@@ -63899,7 +64094,7 @@ async function startServer() {
63899
64094
  if (!sourceKind || !sourceRef) {
63900
64095
  return reply.status(400).send({ ok: false, error: "sourceKind and sourceRef required" });
63901
64096
  }
63902
- const tempHome = (0, import_node_fs29.mkdtempSync)((0, import_node_path27.join)((0, import_node_os19.tmpdir)(), "floless-graft-"));
64097
+ const tempHome = (0, import_node_fs30.mkdtempSync)((0, import_node_path28.join)((0, import_node_os20.tmpdir)(), "floless-graft-"));
63903
64098
  let result;
63904
64099
  try {
63905
64100
  result = await aware.build({
@@ -63912,19 +64107,19 @@ async function startServer() {
63912
64107
  awareHome: tempHome
63913
64108
  });
63914
64109
  } catch (err2) {
63915
- (0, import_node_fs29.rmSync)(tempHome, { recursive: true, force: true });
64110
+ (0, import_node_fs30.rmSync)(tempHome, { recursive: true, force: true });
63916
64111
  const msg = err2 instanceof AwareError ? err2.message : String(err2?.message ?? err2);
63917
64112
  return reply.status(422).send({ ok: false, error: msg });
63918
64113
  }
63919
64114
  const manifest = readStagedManifest(result.agentDir);
63920
64115
  if (!manifest) {
63921
- (0, import_node_fs29.rmSync)(tempHome, { recursive: true, force: true });
64116
+ (0, import_node_fs30.rmSync)(tempHome, { recursive: true, force: true });
63922
64117
  return reply.status(502).send({ ok: false, error: `build produced output at ${result.agentDir} but no manifest.yaml` });
63923
64118
  }
63924
64119
  const token = (0, import_node_crypto7.randomUUID)();
63925
64120
  registerStage(token, tempHome, result.agentId);
63926
64121
  const preview = buildPreview(manifest, sourceKind, sourceRef, token);
63927
- if ((0, import_node_fs29.existsSync)((0, import_node_path27.join)(graftAgentsDir(), result.agentId))) {
64122
+ if ((0, import_node_fs30.existsSync)((0, import_node_path28.join)(graftAgentsDir(), result.agentId))) {
63928
64123
  preview.warnings.unshift(`An agent named "${result.agentId}" is already installed \u2014 creating it will overwrite it.`);
63929
64124
  }
63930
64125
  return { ok: true, preview };
@@ -63943,7 +64138,7 @@ async function startServer() {
63943
64138
  registerStage(stagedRef, stage.tempDir, stage.agentId);
63944
64139
  return reply.status(409).send({ ok: false, error: err2.message, agentId: stage.agentId, collision: true });
63945
64140
  }
63946
- (0, import_node_fs29.rmSync)(stage.tempDir, { recursive: true, force: true });
64141
+ (0, import_node_fs30.rmSync)(stage.tempDir, { recursive: true, force: true });
63947
64142
  throw err2;
63948
64143
  }
63949
64144
  broadcast({ type: "grafted", id: stage.agentId });
@@ -64203,11 +64398,11 @@ async function startServer() {
64203
64398
  app.get("/api/requests/:id/snapshot/:n", async (req, reply) => {
64204
64399
  const n = Number.parseInt(req.params.n, 10);
64205
64400
  const p = Number.isInteger(n) ? snapshotPathFor(req.params.id, n) : null;
64206
- if (!p || !(0, import_node_fs29.existsSync)(p)) return reply.status(404).send({ ok: false, error: "snapshot not found" });
64401
+ if (!p || !(0, import_node_fs30.existsSync)(p)) return reply.status(404).send({ ok: false, error: "snapshot not found" });
64207
64402
  const ext = p.split(".").pop().toLowerCase();
64208
64403
  reply.header("Content-Type", ext === "png" ? "image/png" : ext === "webp" ? "image/webp" : ext === "pdf" ? "application/pdf" : "image/jpeg");
64209
64404
  reply.header("Cache-Control", "no-store");
64210
- return (0, import_node_fs29.readFileSync)(p);
64405
+ return (0, import_node_fs30.readFileSync)(p);
64211
64406
  });
64212
64407
  app.post(
64213
64408
  "/api/tweak",
@@ -64369,7 +64564,7 @@ async function startServer() {
64369
64564
  warn(`last-run-status:${ref} \u2014 trace exists but couldn't be parsed (corrupt/truncated)`);
64370
64565
  let finishedAt2 = null;
64371
64566
  try {
64372
- finishedAt2 = (0, import_node_fs29.statSync)(latest.path).mtime.toISOString();
64567
+ finishedAt2 = (0, import_node_fs30.statSync)(latest.path).mtime.toISOString();
64373
64568
  } catch {
64374
64569
  finishedAt2 = null;
64375
64570
  }
@@ -64379,7 +64574,7 @@ async function startServer() {
64379
64574
  let finishedAt = typeof runEnd?.ts === "string" ? runEnd.ts : null;
64380
64575
  if (!finishedAt) {
64381
64576
  try {
64382
- finishedAt = (0, import_node_fs29.statSync)(latest.path).mtime.toISOString();
64577
+ finishedAt = (0, import_node_fs30.statSync)(latest.path).mtime.toISOString();
64383
64578
  } catch {
64384
64579
  finishedAt = null;
64385
64580
  }