@floless/app 0.18.0 → 0.19.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 } = require("worker_threads");
5492
- var { join: join23 } = require("path");
5492
+ var { join: join24 } = 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"] || join23(__dirname, "lib", "worker.js");
5543
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join24(__dirname, "lib", "worker.js");
5544
5544
  const worker = new Worker(toExecute, {
5545
5545
  ...opts.workerOpts,
5546
5546
  name: opts.workerOpts?.name || "thread-stream",
@@ -6006,9 +6006,9 @@ var require_transport = __commonJS({
6006
6006
  "node_modules/pino/lib/transport.js"(exports2, module2) {
6007
6007
  "use strict";
6008
6008
  var { createRequire: createRequire4 } = require("module");
6009
- var { existsSync: existsSync21 } = require("node:fs");
6009
+ var { existsSync: existsSync22 } = require("node:fs");
6010
6010
  var getCallers = require_caller();
6011
- var { join: join23, isAbsolute: isAbsolute2, sep: sep4 } = require("node:path");
6011
+ var { join: join24, isAbsolute: isAbsolute2, sep: sep4 } = require("node:path");
6012
6012
  var { fileURLToPath: fileURLToPath4 } = require("node:url");
6013
6013
  var sleep = require_atomic_sleep();
6014
6014
  var onExit = require_on_exit_leak_free();
@@ -6080,7 +6080,7 @@ var require_transport = __commonJS({
6080
6080
  return false;
6081
6081
  }
6082
6082
  }
6083
- return isAbsolute2(path) && !existsSync21(path);
6083
+ return isAbsolute2(path) && !existsSync22(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"] || join23(__dirname, "worker.js");
6164
+ target = bundlerOverrides["pino-worker"] || join24(__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"] || join23(__dirname, "worker.js");
6182
+ target = bundlerOverrides["pino-worker"] || join24(__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 join23(__dirname, "..", "file.js");
6205
+ return join24(__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 join23 = ",";
7185
+ let join24 = ",";
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
- join23 = `,
7199
+ join24 = `,
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 += join23;
7207
+ res += join24;
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 += `${join23}"... ${getItemCount(removedKeys)} not stringified"`;
7213
+ res += `${join24}"... ${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
- join23 = `,
7234
+ join24 = `,
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 = join23;
7248
+ separator = join24;
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 = join23;
7254
+ separator = join24;
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 join23 = ",";
7295
+ let join24 = ",";
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
- join23 = `,
7308
+ join24 = `,
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 += join23;
7316
+ res += join24;
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 += `${join23}"... ${getItemCount(removedKeys)} not stringified"`;
7322
+ res += `${join24}"... ${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
- join23 = `,
7335
+ join24 = `,
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 = join23;
7344
+ separator = join24;
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 join24 = `,
7402
+ const join25 = `,
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 += join24;
7409
+ res2 += join25;
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 += `${join24}"... ${getItemCount(removedKeys)} not stringified"`;
7415
+ res2 += `${join25}"... ${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 join23 = `,
7431
+ const join24 = `,
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, join23, maximumBreadth);
7437
+ res += stringifyTypedArray(value, join24, maximumBreadth);
7438
7438
  keys = keys.slice(value.length);
7439
7439
  maximumPropertiesToStringify -= value.length;
7440
- separator = join23;
7440
+ separator = join24;
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 = join23;
7451
+ separator = join24;
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 = join23;
7457
+ separator = join24;
7458
7458
  }
7459
7459
  if (separator !== "") {
7460
7460
  res = `
@@ -41991,7 +41991,7 @@ var require_send = __commonJS({
41991
41991
  var { parseTokenList } = require_parseTokenList();
41992
41992
  var { createHttpError } = require_createHttpError();
41993
41993
  var extname2 = path.extname;
41994
- var join23 = path.join;
41994
+ var join24 = 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(join23(root, path2));
42081
+ path2 = normalize2(join24(root, path2));
42082
42082
  } else {
42083
42083
  if (UP_PATH_REGEXP.test(path2)) {
42084
42084
  debug('malicious path "%s"', path2);
@@ -42361,7 +42361,7 @@ var require_send = __commonJS({
42361
42361
  let err;
42362
42362
  for (let i = 0; i < options.index.length; i++) {
42363
42363
  const index = options.index[i];
42364
- const p = join23(path2, index);
42364
+ const p = join24(path2, index);
42365
42365
  const { error, stat: stat4 } = await tryStat(p);
42366
42366
  if (error) {
42367
42367
  err = error;
@@ -50827,9 +50827,9 @@ var import_node_readline2 = require("node:readline");
50827
50827
 
50828
50828
  // index.ts
50829
50829
  var import_node_url3 = require("node:url");
50830
- var import_node_path21 = require("node:path");
50831
- var import_node_os15 = require("node:os");
50832
- var import_node_fs22 = require("node:fs");
50830
+ var import_node_path22 = require("node:path");
50831
+ var import_node_os16 = require("node:os");
50832
+ var import_node_fs23 = require("node:fs");
50833
50833
  var import_node_child_process6 = require("node:child_process");
50834
50834
 
50835
50835
  // log.mjs
@@ -52683,7 +52683,7 @@ function appVersion() {
52683
52683
  return resolveVersion({
52684
52684
  isSea: isSea2(),
52685
52685
  sqVersionXml: readSqVersionXml(),
52686
- define: true ? "0.18.0" : void 0,
52686
+ define: true ? "0.19.0" : void 0,
52687
52687
  pkgVersion: readPkgVersion()
52688
52688
  });
52689
52689
  }
@@ -52693,7 +52693,7 @@ function resolveChannel(s) {
52693
52693
  return "dev";
52694
52694
  }
52695
52695
  function appChannel() {
52696
- return resolveChannel({ isSea: isSea2(), define: true ? "0.18.0" : void 0 });
52696
+ return resolveChannel({ isSea: isSea2(), define: true ? "0.19.0" : void 0 });
52697
52697
  }
52698
52698
 
52699
52699
  // oauth-presets.ts
@@ -53279,6 +53279,31 @@ function storeVisualInput(appId, dataUrl) {
53279
53279
  }
53280
53280
  return { path, ext, bytes: (0, import_node_fs13.statSync)(path).size, sha256: sha2562 };
53281
53281
  }
53282
+ function renameVisualInputs(oldId, newId) {
53283
+ if (!safeSegment(oldId) || !safeSegment(newId)) throw new VisualInputError("invalid app id");
53284
+ const from = (0, import_node_path11.join)(inputsDir(), oldId);
53285
+ if (!(0, import_node_fs13.existsSync)(from)) return;
53286
+ const to = (0, import_node_path11.join)(inputsDir(), newId);
53287
+ if ((0, import_node_fs13.existsSync)(to)) (0, import_node_fs13.rmSync)(to, { recursive: true, force: true });
53288
+ (0, import_node_fs13.mkdirSync)(inputsDir(), { recursive: true });
53289
+ (0, import_node_fs13.renameSync)(from, to);
53290
+ }
53291
+ function copyVisualInputs(srcId, newId) {
53292
+ if (!safeSegment(srcId) || !safeSegment(newId)) throw new VisualInputError("invalid app id");
53293
+ const from = (0, import_node_path11.join)(inputsDir(), srcId);
53294
+ if (!(0, import_node_fs13.existsSync)(from)) return;
53295
+ (0, import_node_fs13.cpSync)(from, (0, import_node_path11.join)(inputsDir(), newId), { recursive: true });
53296
+ }
53297
+ function deleteVisualInputs(id) {
53298
+ if (!safeSegment(id)) throw new VisualInputError("invalid app id");
53299
+ const dir = (0, import_node_path11.join)(inputsDir(), id);
53300
+ if ((0, import_node_fs13.existsSync)(dir)) (0, import_node_fs13.rmSync)(dir, { recursive: true, force: true });
53301
+ }
53302
+
53303
+ // app-lifecycle.ts
53304
+ var import_node_fs16 = require("node:fs");
53305
+ var import_node_os11 = require("node:os");
53306
+ var import_node_path14 = require("node:path");
53282
53307
 
53283
53308
  // routines.ts
53284
53309
  var import_node_fs15 = require("node:fs");
@@ -53868,6 +53893,28 @@ function deleteRoutine(id) {
53868
53893
  broadcast({ type: "routine-changed", id });
53869
53894
  return true;
53870
53895
  }
53896
+ function repointRoutines(oldId, newId) {
53897
+ ensureLoaded();
53898
+ const changed = routines.filter((r) => r.workflow === oldId);
53899
+ if (changed.length === 0) return 0;
53900
+ const now = (/* @__PURE__ */ new Date()).toISOString();
53901
+ for (const r of changed) {
53902
+ r.workflow = newId;
53903
+ r.updated = now;
53904
+ }
53905
+ saveRoutines();
53906
+ for (const r of changed) {
53907
+ if (r.kind === "trigger") syncTriggerSession(r);
53908
+ broadcast({ type: "routine-changed", id: r.id });
53909
+ }
53910
+ return changed.length;
53911
+ }
53912
+ function removeRoutinesForApp(id) {
53913
+ ensureLoaded();
53914
+ const ids = routines.filter((r) => r.workflow === id).map((r) => r.id);
53915
+ for (const rid of ids) deleteRoutine(rid);
53916
+ return ids.length;
53917
+ }
53871
53918
  function runNow(id) {
53872
53919
  ensureLoaded();
53873
53920
  const r = getRoutine(id);
@@ -54038,6 +54085,167 @@ async function stopScheduler() {
54038
54085
  await stopAllSessions();
54039
54086
  }
54040
54087
 
54088
+ // app-lifecycle.ts
54089
+ var APP_ID3 = /^[a-z0-9][a-z0-9._-]*$/i;
54090
+ var AppLifecycleError = class extends Error {
54091
+ status;
54092
+ constructor(message, status) {
54093
+ super(message);
54094
+ this.name = "AppLifecycleError";
54095
+ this.status = status;
54096
+ }
54097
+ };
54098
+ function appsDir() {
54099
+ return process.env.AWARE_HOME ? (0, import_node_path14.join)(process.env.AWARE_HOME, "apps") : (0, import_node_path14.join)((0, import_node_os11.homedir)(), ".aware", "apps");
54100
+ }
54101
+ function appDirPath(id) {
54102
+ return (0, import_node_path14.join)(appsDir(), id);
54103
+ }
54104
+ function appInstalled(id) {
54105
+ return APP_ID3.test(id) && (0, import_node_fs16.existsSync)(appDirPath(id));
54106
+ }
54107
+ function findSource(dir) {
54108
+ const files = (0, import_node_fs16.readdirSync)(dir);
54109
+ const hit = files.find((f) => f.toLowerCase().endsWith(".app")) ?? files.find((f) => f.toLowerCase().endsWith(".flo"));
54110
+ return hit ? (0, import_node_path14.join)(dir, hit) : null;
54111
+ }
54112
+ function rewriteAppId(text, newId) {
54113
+ const re = /^(app:[ \t]*)(["']?)([^"'#\r\n]+?)\2([ \t]*(?:#[^\r\n]*)?)$/m;
54114
+ if (!re.test(text)) throw new AppLifecycleError("source has no top-level app: field", 422);
54115
+ return text.replace(re, (_m, pre, quote, _id, trail) => `${pre}${quote}${newId}${quote}${trail}`);
54116
+ }
54117
+ function isExposedAsAgent(dir) {
54118
+ const src = findSource(dir);
54119
+ return src ? /^exposes-as-agent:[ \t]*true\b/m.test((0, import_node_fs16.readFileSync)(src, "utf8")) : false;
54120
+ }
54121
+ function logCascade(what, e) {
54122
+ console.error(`[app-lifecycle] cascade step failed (${what}):`, e instanceof Error ? e.message : e);
54123
+ }
54124
+ function removeLocks(dir) {
54125
+ for (const f of (0, import_node_fs16.readdirSync)(dir)) {
54126
+ const lower = f.toLowerCase();
54127
+ if (lower.endsWith(".lock") || lower === "lockfile.yaml") {
54128
+ try {
54129
+ (0, import_node_fs16.unlinkSync)((0, import_node_path14.join)(dir, f));
54130
+ } catch {
54131
+ }
54132
+ }
54133
+ }
54134
+ }
54135
+ function restampSource(dir, newId) {
54136
+ const src = findSource(dir);
54137
+ if (!src) throw new AppLifecycleError("app has no source file", 422);
54138
+ const text = rewriteAppId((0, import_node_fs16.readFileSync)(src, "utf8"), newId);
54139
+ const ext = src.toLowerCase().endsWith(".flo") ? ".flo" : ".app";
54140
+ const newSrc = (0, import_node_path14.join)(dir, `${newId}${ext}`);
54141
+ (0, import_node_fs16.writeFileSync)(newSrc, text);
54142
+ if (src !== newSrc) {
54143
+ try {
54144
+ (0, import_node_fs16.unlinkSync)(src);
54145
+ } catch {
54146
+ }
54147
+ }
54148
+ removeLocks(dir);
54149
+ return newSrc;
54150
+ }
54151
+ function assertNewId(newId) {
54152
+ if (!newId || !APP_ID3.test(newId) || !safeSegment(newId)) {
54153
+ throw new AppLifecycleError('name must be letters, digits, dot, dash or underscore (no spaces, slashes or "..")', 400);
54154
+ }
54155
+ }
54156
+ var defaultDeps2 = {
54157
+ compile: (p) => aware.compile(p),
54158
+ uninstall: (id) => aware.uninstall(id)
54159
+ };
54160
+ async function tryCompile(deps, sourcePath) {
54161
+ try {
54162
+ await deps.compile(sourcePath);
54163
+ return true;
54164
+ } catch {
54165
+ return false;
54166
+ }
54167
+ }
54168
+ async function renameApp(oldId, newId, deps = defaultDeps2) {
54169
+ if (!appInstalled(oldId)) throw new AppLifecycleError(`workflow not found: ${oldId}`, 404);
54170
+ assertNewId(newId);
54171
+ if (newId === oldId) throw new AppLifecycleError("that is already its name", 400);
54172
+ if (appInstalled(newId)) throw new AppLifecycleError(`a workflow named "${newId}" already exists`, 409);
54173
+ if (isExposedAsAgent(appDirPath(oldId))) {
54174
+ throw new AppLifecycleError("this workflow is baked into an agent \u2014 rename isn\u2019t supported yet (the agent wouldn\u2019t follow). Un-bake it first, or rename it from the terminal.", 409);
54175
+ }
54176
+ const oldDir = appDirPath(oldId);
54177
+ const newDir = appDirPath(newId);
54178
+ (0, import_node_fs16.renameSync)(oldDir, newDir);
54179
+ let newSrc;
54180
+ try {
54181
+ newSrc = restampSource(newDir, newId);
54182
+ } catch (e) {
54183
+ try {
54184
+ (0, import_node_fs16.renameSync)(newDir, oldDir);
54185
+ } catch {
54186
+ }
54187
+ throw e;
54188
+ }
54189
+ const compiled = await tryCompile(deps, newSrc);
54190
+ try {
54191
+ renameVisualInputs(oldId, newId);
54192
+ } catch (e) {
54193
+ logCascade("move visual inputs", e);
54194
+ }
54195
+ try {
54196
+ repointRoutines(oldId, newId);
54197
+ } catch (e) {
54198
+ logCascade("re-point routines", e);
54199
+ }
54200
+ return { id: newId, compiled };
54201
+ }
54202
+ async function duplicateApp(srcId, newId, deps = defaultDeps2) {
54203
+ if (!appInstalled(srcId)) throw new AppLifecycleError(`workflow not found: ${srcId}`, 404);
54204
+ assertNewId(newId);
54205
+ if (appInstalled(newId)) throw new AppLifecycleError(`a workflow named "${newId}" already exists`, 409);
54206
+ if (isExposedAsAgent(appDirPath(srcId))) {
54207
+ throw new AppLifecycleError("this workflow is baked into an agent \u2014 duplicate isn\u2019t supported yet (the copy\u2019s agent wouldn\u2019t be installed). Un-bake it first, or copy it from the terminal.", 409);
54208
+ }
54209
+ const newDir = appDirPath(newId);
54210
+ try {
54211
+ (0, import_node_fs16.mkdirSync)(newDir);
54212
+ } catch {
54213
+ throw new AppLifecycleError(`a workflow named "${newId}" already exists`, 409);
54214
+ }
54215
+ let newSrc;
54216
+ try {
54217
+ (0, import_node_fs16.cpSync)(appDirPath(srcId), newDir, { recursive: true });
54218
+ newSrc = restampSource(newDir, newId);
54219
+ } catch (e) {
54220
+ try {
54221
+ (0, import_node_fs16.rmSync)(newDir, { recursive: true, force: true });
54222
+ } catch {
54223
+ }
54224
+ throw e;
54225
+ }
54226
+ const compiled = await tryCompile(deps, newSrc);
54227
+ try {
54228
+ copyVisualInputs(srcId, newId);
54229
+ } catch (e) {
54230
+ logCascade("copy visual inputs", e);
54231
+ }
54232
+ return { id: newId, compiled };
54233
+ }
54234
+ async function deleteApp(id, deps = defaultDeps2) {
54235
+ if (!appInstalled(id)) throw new AppLifecycleError(`workflow not found: ${id}`, 404);
54236
+ await deps.uninstall(id);
54237
+ try {
54238
+ deleteVisualInputs(id);
54239
+ } catch (e) {
54240
+ logCascade("remove visual inputs", e);
54241
+ }
54242
+ try {
54243
+ removeRoutinesForApp(id);
54244
+ } catch (e) {
54245
+ logCascade("remove routines", e);
54246
+ }
54247
+ }
54248
+
54041
54249
  // route-gate.ts
54042
54250
  var AWARE_ROUTES = [
54043
54251
  "/api/apps",
@@ -54063,14 +54271,15 @@ function isGatedAwareRoute(url, method) {
54063
54271
  }
54064
54272
  if (path.startsWith("/api/trigger-run/")) return false;
54065
54273
  if (path === "/api/trigger-run") return m === "POST";
54274
+ if (path.startsWith("/api/app/")) return m === "PATCH" || m === "POST" || m === "DELETE";
54066
54275
  return AWARE_ROUTES.some((p) => path.startsWith(p));
54067
54276
  }
54068
54277
 
54069
54278
  // autostart.mjs
54070
54279
  var import_node_child_process3 = require("node:child_process");
54071
- var import_node_fs16 = require("node:fs");
54072
- var import_node_os11 = require("node:os");
54073
- var import_node_path14 = require("node:path");
54280
+ var import_node_fs17 = require("node:fs");
54281
+ var import_node_os12 = require("node:os");
54282
+ var import_node_path15 = require("node:path");
54074
54283
 
54075
54284
  // teardown.mjs
54076
54285
  var RUN_KEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
@@ -54182,8 +54391,8 @@ function removeLegacyRunKey() {
54182
54391
  }
54183
54392
  function logLine(msg) {
54184
54393
  try {
54185
- (0, import_node_fs16.mkdirSync)(logDir(), { recursive: true });
54186
- (0, import_node_fs16.appendFileSync)(logFilePath(), `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
54394
+ (0, import_node_fs17.mkdirSync)(logDir(), { recursive: true });
54395
+ (0, import_node_fs17.appendFileSync)(logFilePath(), `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
54187
54396
  `);
54188
54397
  } catch {
54189
54398
  }
@@ -54191,8 +54400,8 @@ function logLine(msg) {
54191
54400
  function registerAutostart(exePath) {
54192
54401
  if (!isWin) return;
54193
54402
  const xml = buildAutostartTaskXml(exePath, currentUserId());
54194
- const tmp = (0, import_node_path14.join)((0, import_node_os11.tmpdir)(), `floless-autostart-${process.pid}-${Date.now()}.xml`);
54195
- (0, import_node_fs16.writeFileSync)(tmp, "\uFEFF" + xml, { encoding: "utf16le" });
54403
+ const tmp = (0, import_node_path15.join)((0, import_node_os12.tmpdir)(), `floless-autostart-${process.pid}-${Date.now()}.xml`);
54404
+ (0, import_node_fs17.writeFileSync)(tmp, "\uFEFF" + xml, { encoding: "utf16le" });
54196
54405
  try {
54197
54406
  (0, import_node_child_process3.execFileSync)("schtasks", ["/Create", "/TN", TASK_NAME, "/XML", tmp, "/F"], {
54198
54407
  stdio: ["ignore", "ignore", "ignore"],
@@ -54203,7 +54412,7 @@ function registerAutostart(exePath) {
54203
54412
  throw err;
54204
54413
  } finally {
54205
54414
  try {
54206
- (0, import_node_fs16.rmSync)(tmp, { force: true });
54415
+ (0, import_node_fs17.rmSync)(tmp, { force: true });
54207
54416
  } catch {
54208
54417
  }
54209
54418
  }
@@ -54238,26 +54447,26 @@ function unregisterAutostart() {
54238
54447
  // updater.ts
54239
54448
  var import_node_child_process4 = require("node:child_process");
54240
54449
  var import_node_crypto6 = require("node:crypto");
54241
- var import_node_fs18 = require("node:fs");
54450
+ var import_node_fs19 = require("node:fs");
54242
54451
  var import_node_stream = require("node:stream");
54243
54452
  var import_promises = require("node:stream/promises");
54244
- var import_node_path16 = require("node:path");
54453
+ var import_node_path17 = require("node:path");
54245
54454
 
54246
54455
  // post-update-marker.mjs
54247
- var import_node_fs17 = require("node:fs");
54248
- var import_node_os12 = require("node:os");
54249
- var import_node_path15 = require("node:path");
54456
+ var import_node_fs18 = require("node:fs");
54457
+ var import_node_os13 = require("node:os");
54458
+ var import_node_path16 = require("node:path");
54250
54459
  var FRESH_MS = 12e4;
54251
54460
  function markerPath() {
54252
54461
  const override = (process.env.FLOLESS_POST_UPDATE_MARKER ?? "").trim();
54253
54462
  if (override) return override;
54254
- const root = process.env.FLOLESS_HOME ?? (0, import_node_path15.join)((0, import_node_os12.homedir)(), ".floless");
54255
- return (0, import_node_path15.join)(root, ".post-update");
54463
+ const root = process.env.FLOLESS_HOME ?? (0, import_node_path16.join)((0, import_node_os13.homedir)(), ".floless");
54464
+ return (0, import_node_path16.join)(root, ".post-update");
54256
54465
  }
54257
54466
  function legacyMarkerPath() {
54258
54467
  if ((process.env.FLOLESS_POST_UPDATE_MARKER ?? "").trim()) return null;
54259
54468
  try {
54260
- return (0, import_node_path15.join)((0, import_node_path15.dirname)((0, import_node_path15.dirname)(process.execPath)), ".floless-post-update");
54469
+ return (0, import_node_path16.join)((0, import_node_path16.dirname)((0, import_node_path16.dirname)(process.execPath)), ".floless-post-update");
54261
54470
  } catch {
54262
54471
  return null;
54263
54472
  }
@@ -54267,7 +54476,7 @@ function writePostUpdateMarker() {
54267
54476
  for (const p of [markerPath(), legacyMarkerPath()]) {
54268
54477
  if (!p) continue;
54269
54478
  try {
54270
- (0, import_node_fs17.writeFileSync)(p, (/* @__PURE__ */ new Date()).toISOString());
54479
+ (0, import_node_fs18.writeFileSync)(p, (/* @__PURE__ */ new Date()).toISOString());
54271
54480
  wrote = true;
54272
54481
  } catch {
54273
54482
  }
@@ -54279,9 +54488,9 @@ function consumePostUpdateMarker() {
54279
54488
  for (const p of [markerPath(), legacyMarkerPath()]) {
54280
54489
  if (!p) continue;
54281
54490
  try {
54282
- if (!(0, import_node_fs17.existsSync)(p)) continue;
54283
- const ageMs = Date.now() - (0, import_node_fs17.statSync)(p).mtimeMs;
54284
- (0, import_node_fs17.rmSync)(p, { force: true });
54491
+ if (!(0, import_node_fs18.existsSync)(p)) continue;
54492
+ const ageMs = Date.now() - (0, import_node_fs18.statSync)(p).mtimeMs;
54493
+ (0, import_node_fs18.rmSync)(p, { force: true });
54285
54494
  if (ageMs < FRESH_MS) fresh = true;
54286
54495
  } catch {
54287
54496
  }
@@ -54298,13 +54507,13 @@ function currentVersion() {
54298
54507
  return appVersion();
54299
54508
  }
54300
54509
  function installRoot() {
54301
- return (0, import_node_path16.dirname)((0, import_node_path16.dirname)(process.execPath));
54510
+ return (0, import_node_path17.dirname)((0, import_node_path17.dirname)(process.execPath));
54302
54511
  }
54303
54512
  function updateExePath() {
54304
- return (0, import_node_path16.join)(installRoot(), "Update.exe");
54513
+ return (0, import_node_path17.join)(installRoot(), "Update.exe");
54305
54514
  }
54306
54515
  function packagesDir() {
54307
- return (0, import_node_path16.join)(installRoot(), "packages");
54516
+ return (0, import_node_path17.join)(installRoot(), "packages");
54308
54517
  }
54309
54518
  function feedUrl() {
54310
54519
  const env2 = (process.env.FLOLESS_UPDATE_URL ?? "").trim().replace(/\/+$/, "");
@@ -54397,22 +54606,22 @@ async function checkForUpdate() {
54397
54606
  }
54398
54607
  async function sha1OfFile(path) {
54399
54608
  const hash = (0, import_node_crypto6.createHash)("sha1");
54400
- await (0, import_promises.pipeline)((0, import_node_fs18.createReadStream)(path), hash);
54609
+ await (0, import_promises.pipeline)((0, import_node_fs19.createReadStream)(path), hash);
54401
54610
  return hash.digest("hex").toUpperCase();
54402
54611
  }
54403
54612
  async function downloadPackage(asset) {
54404
54613
  if (!NUPKG_NAME.test(asset.FileName)) throw new Error(`refusing suspicious package name: ${asset.FileName}`);
54405
54614
  const want = asset.SHA1.toUpperCase();
54406
54615
  const dir = packagesDir();
54407
- (0, import_node_fs18.mkdirSync)(dir, { recursive: true });
54408
- const dest = (0, import_node_path16.join)(dir, asset.FileName);
54409
- if ((0, import_node_fs18.existsSync)(dest) && await sha1OfFile(dest) === want) return dest;
54616
+ (0, import_node_fs19.mkdirSync)(dir, { recursive: true });
54617
+ const dest = (0, import_node_path17.join)(dir, asset.FileName);
54618
+ if ((0, import_node_fs19.existsSync)(dest) && await sha1OfFile(dest) === want) return dest;
54410
54619
  const res = await authedFetch(`${feedUrl()}/${encodeURIComponent(asset.FileName)}`, {
54411
54620
  redirect: "follow",
54412
54621
  signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS)
54413
54622
  });
54414
54623
  if (!res.ok || !res.body) throw new Error(`download failed: HTTP ${res.status} for ${asset.FileName}`);
54415
- await (0, import_promises.pipeline)(import_node_stream.Readable.fromWeb(res.body), (0, import_node_fs18.createWriteStream)(dest));
54624
+ await (0, import_promises.pipeline)(import_node_stream.Readable.fromWeb(res.body), (0, import_node_fs19.createWriteStream)(dest));
54416
54625
  const got = await sha1OfFile(dest);
54417
54626
  if (got !== want) throw new Error(`SHA1 mismatch for ${asset.FileName}: feed=${want} got=${got}`);
54418
54627
  return dest;
@@ -54421,7 +54630,7 @@ async function applyUpdate(check, opts) {
54421
54630
  if (!check.supported) return { applied: false, message: check.reason ?? "auto-update not supported in this runtime" };
54422
54631
  if (!check.updateAvailable || !check.asset) return { applied: false, message: check.reason ?? "no update available" };
54423
54632
  const exe = updateExePath();
54424
- if (!(0, import_node_fs18.existsSync)(exe)) {
54633
+ if (!(0, import_node_fs19.existsSync)(exe)) {
54425
54634
  return { applied: false, message: `Update.exe not found at ${exe} \u2014 is this a Velopack install?` };
54426
54635
  }
54427
54636
  const pkg = await downloadPackage(check.asset);
@@ -54630,12 +54839,12 @@ function isTraceCorrupt(events) {
54630
54839
 
54631
54840
  // launch.mjs
54632
54841
  var import_node_child_process5 = require("node:child_process");
54633
- var import_node_path17 = require("node:path");
54842
+ var import_node_path18 = require("node:path");
54634
54843
  var import_node_url = require("node:url");
54635
- var import_node_fs19 = require("node:fs");
54844
+ var import_node_fs20 = require("node:fs");
54636
54845
  var import_node_http = __toESM(require("node:http"), 1);
54637
54846
  var import_node_readline = require("node:readline");
54638
- var __dirname2 = (0, import_node_path17.dirname)((0, import_node_url.fileURLToPath)(__import_meta_url));
54847
+ var __dirname2 = (0, import_node_path18.dirname)((0, import_node_url.fileURLToPath)(__import_meta_url));
54639
54848
  var PORT = Number(process.env.PORT ?? 4317);
54640
54849
  var HEALTH_URL = `http://127.0.0.1:${PORT}/api/health`;
54641
54850
  var BROWSER_URL = `http://floless.localhost:${PORT}`;
@@ -54707,8 +54916,8 @@ async function waitHealthy(timeoutMs = 3e4) {
54707
54916
  function resolveServerStart() {
54708
54917
  const packaged = /flolessapp\.exe$/i.test(process.execPath);
54709
54918
  if (packaged) return { cmd: process.execPath, args: ["--serve"], shell: false };
54710
- const bundle = (0, import_node_path17.join)(__dirname2, "dist", "floless-server.cjs");
54711
- if ((0, import_node_fs19.existsSync)(bundle)) return { cmd: process.execPath, args: [bundle, "--serve"], shell: false };
54919
+ const bundle = (0, import_node_path18.join)(__dirname2, "dist", "floless-server.cjs");
54920
+ if ((0, import_node_fs20.existsSync)(bundle)) return { cmd: process.execPath, args: [bundle, "--serve"], shell: false };
54712
54921
  return { cmd: "npm", args: ["run", "start"], shell: isWin2 };
54713
54922
  }
54714
54923
  function startServerDetached() {
@@ -54861,8 +55070,8 @@ function taskkillArgs(pid, { tree = true } = {}) {
54861
55070
  }
54862
55071
  function killSupervisor({ tree = true } = {}) {
54863
55072
  if (!isWin2) return;
54864
- const isNpmChannel = /^node(\.exe)?$/i.test((0, import_node_path17.basename)(process.execPath));
54865
- const scriptMatch = isNpmChannel ? (0, import_node_path17.basename)((0, import_node_url.fileURLToPath)(__import_meta_url)) : void 0;
55073
+ const isNpmChannel = /^node(\.exe)?$/i.test((0, import_node_path18.basename)(process.execPath));
55074
+ const scriptMatch = isNpmChannel ? (0, import_node_path18.basename)((0, import_node_url.fileURLToPath)(__import_meta_url)) : void 0;
54866
55075
  const realExe = resolveRealInstallExe(process.execPath);
54867
55076
  const exeMatch = realExe === process.execPath ? process.execPath : [process.execPath, realExe];
54868
55077
  const pids = supervisorPidsToKill(enumerateProcesses(), process.pid, exeMatch, scriptMatch);
@@ -55027,7 +55236,7 @@ async function runAction(arg, flagArgv = [], selfVersion = null) {
55027
55236
  }
55028
55237
  await action(parseTeardownFlags(flagArgv));
55029
55238
  }
55030
- var entry = (0, import_node_path17.basename)(process.argv[1] ?? "").toLowerCase();
55239
+ var entry = (0, import_node_path18.basename)(process.argv[1] ?? "").toLowerCase();
55031
55240
  if (entry === "launch.mjs") {
55032
55241
  runAction(process.argv[2], process.argv.slice(3)).catch((e) => {
55033
55242
  log(`error: ${e?.message ?? e}`);
@@ -55101,9 +55310,9 @@ function awareUpgradeBlockReason(s) {
55101
55310
  }
55102
55311
 
55103
55312
  // skill-sync.ts
55104
- var import_node_fs20 = require("node:fs");
55105
- var import_node_os13 = require("node:os");
55106
- var import_node_path18 = require("node:path");
55313
+ var import_node_fs21 = require("node:fs");
55314
+ var import_node_os14 = require("node:os");
55315
+ var import_node_path19 = require("node:path");
55107
55316
  var import_node_url2 = require("node:url");
55108
55317
  var import_yaml5 = __toESM(require_dist6(), 1);
55109
55318
 
@@ -55130,30 +55339,30 @@ function selectShippedSkillNames(names) {
55130
55339
  }
55131
55340
 
55132
55341
  // skill-sync.ts
55133
- var __dirname3 = (0, import_node_path18.dirname)((0, import_node_url2.fileURLToPath)(__import_meta_url));
55342
+ var __dirname3 = (0, import_node_path19.dirname)((0, import_node_url2.fileURLToPath)(__import_meta_url));
55134
55343
  function bundledSkillsRoot() {
55135
55344
  const candidates = [
55136
- (0, import_node_path18.join)(__dirname3, "skills"),
55137
- (0, import_node_path18.join)((0, import_node_path18.dirname)(process.execPath), "skills"),
55138
- (0, import_node_path18.join)(__dirname3, "..", ".claude", "skills")
55345
+ (0, import_node_path19.join)(__dirname3, "skills"),
55346
+ (0, import_node_path19.join)((0, import_node_path19.dirname)(process.execPath), "skills"),
55347
+ (0, import_node_path19.join)(__dirname3, "..", ".claude", "skills")
55139
55348
  ];
55140
- return candidates.find((p) => (0, import_node_fs20.existsSync)(p)) ?? null;
55349
+ return candidates.find((p) => (0, import_node_fs21.existsSync)(p)) ?? null;
55141
55350
  }
55142
55351
  function targetConfigDirs() {
55143
55352
  const override = process.env.FLOLESS_SKILL_TARGETS;
55144
55353
  if (override) {
55145
55354
  return override.split(";").map((d) => d.trim()).filter(Boolean).map((dir) => ({ runtime: "custom", dir }));
55146
55355
  }
55147
- const home = (0, import_node_os13.homedir)();
55356
+ const home = (0, import_node_os14.homedir)();
55148
55357
  return [
55149
- { runtime: "claude", dir: (0, import_node_path18.join)(home, ".claude") },
55150
- { runtime: "codex", dir: (0, import_node_path18.join)(home, ".codex") },
55151
- { runtime: "opencode", dir: (0, import_node_path18.join)(home, ".opencode") }
55358
+ { runtime: "claude", dir: (0, import_node_path19.join)(home, ".claude") },
55359
+ { runtime: "codex", dir: (0, import_node_path19.join)(home, ".codex") },
55360
+ { runtime: "opencode", dir: (0, import_node_path19.join)(home, ".opencode") }
55152
55361
  ];
55153
55362
  }
55154
55363
  function skillVersion(skillMdPath) {
55155
55364
  try {
55156
- const text = (0, import_node_fs20.readFileSync)(skillMdPath, "utf8");
55365
+ const text = (0, import_node_fs21.readFileSync)(skillMdPath, "utf8");
55157
55366
  const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(text);
55158
55367
  if (!m || m[1] === void 0) return null;
55159
55368
  const fm = (0, import_yaml5.parse)(m[1]);
@@ -55193,21 +55402,21 @@ function decideAction(installed, bundled) {
55193
55402
  function bundledSkills(root) {
55194
55403
  let entries = [];
55195
55404
  try {
55196
- entries = selectShippedSkillNames((0, import_node_fs20.readdirSync)(root));
55405
+ entries = selectShippedSkillNames((0, import_node_fs21.readdirSync)(root));
55197
55406
  } catch {
55198
55407
  return [];
55199
55408
  }
55200
55409
  const out = [];
55201
55410
  for (const name of entries) {
55202
- const dir = (0, import_node_path18.join)(root, name);
55411
+ const dir = (0, import_node_path19.join)(root, name);
55203
55412
  let isDir = false;
55204
55413
  try {
55205
- isDir = (0, import_node_fs20.statSync)(dir).isDirectory();
55414
+ isDir = (0, import_node_fs21.statSync)(dir).isDirectory();
55206
55415
  } catch {
55207
55416
  isDir = false;
55208
55417
  }
55209
55418
  if (!isDir) continue;
55210
- const v = skillVersion((0, import_node_path18.join)(dir, "SKILL.md"));
55419
+ const v = skillVersion((0, import_node_path19.join)(dir, "SKILL.md"));
55211
55420
  if (!v) continue;
55212
55421
  out.push({ name, dir, version: v });
55213
55422
  }
@@ -55220,17 +55429,17 @@ function syncSkills() {
55220
55429
  const skills = bundledSkills(root);
55221
55430
  if (!skills.length) return results;
55222
55431
  for (const { runtime, dir: cfg } of targetConfigDirs()) {
55223
- if (!(0, import_node_fs20.existsSync)(cfg)) continue;
55224
- const skillsDir = (0, import_node_path18.join)(cfg, "skills");
55432
+ if (!(0, import_node_fs21.existsSync)(cfg)) continue;
55433
+ const skillsDir = (0, import_node_path19.join)(cfg, "skills");
55225
55434
  for (const s of skills) {
55226
- const dest = (0, import_node_path18.join)(skillsDir, s.name);
55227
- const installedMd = (0, import_node_path18.join)(dest, "SKILL.md");
55228
- const installed = (0, import_node_fs20.existsSync)(installedMd) ? skillVersion(installedMd) : null;
55435
+ const dest = (0, import_node_path19.join)(skillsDir, s.name);
55436
+ const installedMd = (0, import_node_path19.join)(dest, "SKILL.md");
55437
+ const installed = (0, import_node_fs21.existsSync)(installedMd) ? skillVersion(installedMd) : null;
55229
55438
  const action = decideAction(installed, s.version);
55230
55439
  if (action === "installed" || action === "updated") {
55231
55440
  try {
55232
- if (action === "updated") (0, import_node_fs20.rmSync)(dest, { recursive: true, force: true });
55233
- (0, import_node_fs20.cpSync)(s.dir, dest, { recursive: true });
55441
+ if (action === "updated") (0, import_node_fs21.rmSync)(dest, { recursive: true, force: true });
55442
+ (0, import_node_fs21.cpSync)(s.dir, dest, { recursive: true });
55234
55443
  results.push({ runtime, skill: s.name, action, from: installed, to: s.version });
55235
55444
  } catch {
55236
55445
  }
@@ -55243,9 +55452,9 @@ function syncSkills() {
55243
55452
  }
55244
55453
 
55245
55454
  // watch.ts
55246
- var import_node_os14 = require("node:os");
55247
- var import_node_path20 = require("node:path");
55248
- var import_node_fs21 = require("node:fs");
55455
+ var import_node_os15 = require("node:os");
55456
+ var import_node_path21 = require("node:path");
55457
+ var import_node_fs22 = require("node:fs");
55249
55458
 
55250
55459
  // node_modules/chokidar/esm/index.js
55251
55460
  var import_fs2 = require("fs");
@@ -55256,7 +55465,7 @@ var sysPath2 = __toESM(require("path"), 1);
55256
55465
  // node_modules/readdirp/esm/index.js
55257
55466
  var import_promises2 = require("node:fs/promises");
55258
55467
  var import_node_stream2 = require("node:stream");
55259
- var import_node_path19 = require("node:path");
55468
+ var import_node_path20 = require("node:path");
55260
55469
  var EntryTypes = {
55261
55470
  FILE_TYPE: "files",
55262
55471
  DIR_TYPE: "directories",
@@ -55331,7 +55540,7 @@ var ReaddirpStream = class extends import_node_stream2.Readable {
55331
55540
  this._wantsDir = type ? DIR_TYPES.has(type) : false;
55332
55541
  this._wantsFile = type ? FILE_TYPES.has(type) : false;
55333
55542
  this._wantsEverything = type === EntryTypes.EVERYTHING_TYPE;
55334
- this._root = (0, import_node_path19.resolve)(root);
55543
+ this._root = (0, import_node_path20.resolve)(root);
55335
55544
  this._isDirent = !opts.alwaysStat;
55336
55545
  this._statsProp = this._isDirent ? "dirent" : "stats";
55337
55546
  this._rdOptions = { encoding: "utf8", withFileTypes: this._isDirent };
@@ -55402,8 +55611,8 @@ var ReaddirpStream = class extends import_node_stream2.Readable {
55402
55611
  let entry2;
55403
55612
  const basename5 = this._isDirent ? dirent.name : dirent;
55404
55613
  try {
55405
- const fullPath = (0, import_node_path19.resolve)((0, import_node_path19.join)(path, basename5));
55406
- entry2 = { path: (0, import_node_path19.relative)(this._root, fullPath), fullPath, basename: basename5 };
55614
+ const fullPath = (0, import_node_path20.resolve)((0, import_node_path20.join)(path, basename5));
55615
+ entry2 = { path: (0, import_node_path20.relative)(this._root, fullPath), fullPath, basename: basename5 };
55407
55616
  entry2[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
55408
55617
  } catch (err) {
55409
55618
  this._onError(err);
@@ -55437,7 +55646,7 @@ var ReaddirpStream = class extends import_node_stream2.Readable {
55437
55646
  }
55438
55647
  if (entryRealPathStats.isDirectory()) {
55439
55648
  const len = entryRealPath.length;
55440
- if (full.startsWith(entryRealPath) && full.substr(len, 1) === import_node_path19.sep) {
55649
+ if (full.startsWith(entryRealPath) && full.substr(len, 1) === import_node_path20.sep) {
55441
55650
  const recursiveError = new Error(`Circular symlink detected: "${full}" points to "${entryRealPath}"`);
55442
55651
  recursiveError.code = RECURSIVE_ERROR_CODE;
55443
55652
  return this._onError(recursiveError);
@@ -56945,33 +57154,33 @@ function appIdFromLogPath(path) {
56945
57154
  return i >= 0 && parts[i + 1] ? parts[i + 1] : null;
56946
57155
  }
56947
57156
  function samePath(a, b) {
56948
- const ra = (0, import_node_path20.resolve)(a);
56949
- const rb = (0, import_node_path20.resolve)(b);
57157
+ const ra = (0, import_node_path21.resolve)(a);
57158
+ const rb = (0, import_node_path21.resolve)(b);
56950
57159
  return process.platform === "win32" ? ra.toLowerCase() === rb.toLowerCase() : ra === rb;
56951
57160
  }
56952
57161
  function underDir(path, dir) {
56953
- const rp = (0, import_node_path20.resolve)(path);
56954
- const rd = (0, import_node_path20.resolve)(dir);
57162
+ const rp = (0, import_node_path21.resolve)(path);
57163
+ const rd = (0, import_node_path21.resolve)(dir);
56955
57164
  const [p, d] = process.platform === "win32" ? [rp.toLowerCase(), rd.toLowerCase()] : [rp, rd];
56956
- return p === d || p.startsWith(d + import_node_path20.sep);
57165
+ return p === d || p.startsWith(d + import_node_path21.sep);
56957
57166
  }
56958
57167
  function startWatcher() {
56959
- const awareDir = process.env.AWARE_HOME ?? (0, import_node_path20.join)((0, import_node_os14.homedir)(), ".aware");
56960
- const credentialsDir = (0, import_node_path20.join)(awareDir, "credentials");
56961
- if (!(0, import_node_fs21.existsSync)(credentialsDir)) {
57168
+ const awareDir = process.env.AWARE_HOME ?? (0, import_node_path21.join)((0, import_node_os15.homedir)(), ".aware");
57169
+ const credentialsDir = (0, import_node_path21.join)(awareDir, "credentials");
57170
+ if (!(0, import_node_fs22.existsSync)(credentialsDir)) {
56962
57171
  try {
56963
- (0, import_node_fs21.mkdirSync)(credentialsDir, { recursive: true });
57172
+ (0, import_node_fs22.mkdirSync)(credentialsDir, { recursive: true });
56964
57173
  } catch {
56965
57174
  }
56966
57175
  }
56967
- if (!(0, import_node_fs21.existsSync)(uiDir)) {
57176
+ if (!(0, import_node_fs22.existsSync)(uiDir)) {
56968
57177
  try {
56969
- (0, import_node_fs21.mkdirSync)(uiDir, { recursive: true });
57178
+ (0, import_node_fs22.mkdirSync)(uiDir, { recursive: true });
56970
57179
  } catch {
56971
57180
  }
56972
57181
  }
56973
- const targets = ["apps", "logs", "credentials"].map((d) => (0, import_node_path20.join)(awareDir, d)).filter((p) => (0, import_node_fs21.existsSync)(p));
56974
- if ((0, import_node_fs21.existsSync)(uiDir)) targets.push(uiDir);
57182
+ const targets = ["apps", "logs", "credentials"].map((d) => (0, import_node_path21.join)(awareDir, d)).filter((p) => (0, import_node_fs22.existsSync)(p));
57183
+ if ((0, import_node_fs22.existsSync)(uiDir)) targets.push(uiDir);
56975
57184
  if (targets.length === 0) {
56976
57185
  return null;
56977
57186
  }
@@ -57001,11 +57210,11 @@ function startWatcher() {
57001
57210
  const isCredential = path.split(/[\\/]/).includes("credentials");
57002
57211
  const kind = isCredential ? "credential" : path.endsWith(".jsonl") ? "trace" : path.endsWith(".lock") ? "lock" : path.endsWith(".flo") || path.endsWith(".app") ? "source" : "file";
57003
57212
  broadcast({ type: "fs-change", kind, event, path });
57004
- if (kind === "trace" && event !== "unlink" && (0, import_node_fs21.existsSync)(path)) {
57213
+ if (kind === "trace" && event !== "unlink" && (0, import_node_fs22.existsSync)(path)) {
57005
57214
  const id = appIdFromLogPath(path);
57006
57215
  if (!id) return;
57007
57216
  try {
57008
- broadcast({ type: "trace-file", id, runId: path.split(import_node_path20.sep).pop()?.replace(/\.jsonl$/, "") ?? null, events: parseTrace((0, import_node_fs21.readFileSync)(path, "utf8")) });
57217
+ broadcast({ type: "trace-file", id, runId: path.split(import_node_path21.sep).pop()?.replace(/\.jsonl$/, "") ?? null, events: parseTrace((0, import_node_fs22.readFileSync)(path, "utf8")) });
57009
57218
  } catch {
57010
57219
  }
57011
57220
  }
@@ -57014,10 +57223,10 @@ function startWatcher() {
57014
57223
  }
57015
57224
 
57016
57225
  // index.ts
57017
- var __dirname4 = (0, import_node_path21.dirname)((0, import_node_url3.fileURLToPath)(__import_meta_url));
57018
- var WEB_ROOT = [(0, import_node_path21.join)(__dirname4, "web"), (0, import_node_path21.join)((0, import_node_path21.dirname)(process.execPath), "web"), (0, import_node_path21.join)(__dirname4, "..", "web")].find(
57019
- (p) => (0, import_node_fs22.existsSync)(p)
57020
- ) ?? (0, import_node_path21.join)(__dirname4, "..", "web");
57226
+ var __dirname4 = (0, import_node_path22.dirname)((0, import_node_url3.fileURLToPath)(__import_meta_url));
57227
+ var WEB_ROOT = [(0, import_node_path22.join)(__dirname4, "web"), (0, import_node_path22.join)((0, import_node_path22.dirname)(process.execPath), "web"), (0, import_node_path22.join)(__dirname4, "..", "web")].find(
57228
+ (p) => (0, import_node_fs23.existsSync)(p)
57229
+ ) ?? (0, import_node_path22.join)(__dirname4, "..", "web");
57021
57230
  var PORT2 = Number(process.env.PORT ?? 4317);
57022
57231
  var HOST = "127.0.0.1";
57023
57232
  var crashHandlersInstalled = false;
@@ -57033,7 +57242,7 @@ function installCrashHandlers() {
57033
57242
  ${stack}
57034
57243
  `;
57035
57244
  try {
57036
- (0, import_node_fs22.appendFileSync)(logFilePath(), line);
57245
+ (0, import_node_fs23.appendFileSync)(logFilePath(), line);
57037
57246
  } catch {
57038
57247
  }
57039
57248
  if (process.stderr.isTTY) process.stderr.write(line);
@@ -57116,6 +57325,9 @@ async function startServer() {
57116
57325
  if (err instanceof RoutineError) {
57117
57326
  return reply.status(err.status).send({ ok: false, error: err.message });
57118
57327
  }
57328
+ if (err instanceof AppLifecycleError) {
57329
+ return reply.status(err.status).send({ ok: false, error: err.message });
57330
+ }
57119
57331
  if (err instanceof AwareError) {
57120
57332
  app.log.warn({ detail: err.detail }, err.message);
57121
57333
  return reply.status(502).send({ ok: false, error: err.message, detail: err.detail ?? null });
@@ -57361,6 +57573,35 @@ async function startServer() {
57361
57573
  app.get("/api/app/:id", async (req) => {
57362
57574
  return { ok: true, app: readApp(req.params.id) };
57363
57575
  });
57576
+ async function stopForegroundSession(id) {
57577
+ try {
57578
+ const sid = foregroundSessionId(id);
57579
+ if (getSnapshot(sid)) await stopSession(sid);
57580
+ } catch (e) {
57581
+ app.log.warn({ id, err: e instanceof Error ? e.message : String(e) }, "stopForegroundSession failed before lifecycle op");
57582
+ }
57583
+ }
57584
+ app.patch("/api/app/:id/rename", async (req, reply) => {
57585
+ const name = typeof req.body?.name === "string" ? req.body.name.trim() : "";
57586
+ if (!name) return reply.status(400).send({ ok: false, error: "name required" });
57587
+ await stopForegroundSession(req.params.id);
57588
+ const res = await renameApp(req.params.id, name);
57589
+ broadcast({ type: "apps-changed", id: res.id });
57590
+ return { ok: true, ...res };
57591
+ });
57592
+ app.post("/api/app/:id/duplicate", async (req, reply) => {
57593
+ const name = typeof req.body?.name === "string" ? req.body.name.trim() : "";
57594
+ if (!name) return reply.status(400).send({ ok: false, error: "name required" });
57595
+ const res = await duplicateApp(req.params.id, name);
57596
+ broadcast({ type: "apps-changed", id: res.id });
57597
+ return { ok: true, ...res };
57598
+ });
57599
+ app.delete("/api/app/:id", async (req) => {
57600
+ await stopForegroundSession(req.params.id);
57601
+ await deleteApp(req.params.id);
57602
+ broadcast({ type: "apps-changed", id: req.params.id });
57603
+ return { ok: true };
57604
+ });
57364
57605
  app.post("/api/compile", async (req, reply) => {
57365
57606
  const { id, sourcePath } = req.body ?? {};
57366
57607
  const path = sourcePath ?? (id ? readApp(id).source.path : void 0);
@@ -57383,11 +57624,11 @@ async function startServer() {
57383
57624
  if (appExists(id)) {
57384
57625
  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 });
57385
57626
  }
57386
- const stageRoot = (0, import_node_fs22.mkdtempSync)((0, import_node_path21.join)((0, import_node_os15.tmpdir)(), "floless-import-"));
57627
+ const stageRoot = (0, import_node_fs23.mkdtempSync)((0, import_node_path22.join)((0, import_node_os16.tmpdir)(), "floless-import-"));
57387
57628
  try {
57388
- const stageDir = (0, import_node_path21.join)(stageRoot, id);
57389
- (0, import_node_fs22.mkdirSync)(stageDir);
57390
- (0, import_node_fs22.writeFileSync)((0, import_node_path21.join)(stageDir, `${id}.flo`), content);
57629
+ const stageDir = (0, import_node_path22.join)(stageRoot, id);
57630
+ (0, import_node_fs23.mkdirSync)(stageDir);
57631
+ (0, import_node_fs23.writeFileSync)((0, import_node_path22.join)(stageDir, `${id}.flo`), content);
57391
57632
  await aware.install(stageDir);
57392
57633
  } catch (err) {
57393
57634
  try {
@@ -57397,7 +57638,7 @@ async function startServer() {
57397
57638
  const msg = err instanceof AwareError ? err.message : String(err?.message ?? err);
57398
57639
  return reply.status(502).send({ ok: false, error: `import failed: ${msg}` });
57399
57640
  } finally {
57400
- (0, import_node_fs22.rmSync)(stageRoot, { recursive: true, force: true });
57641
+ (0, import_node_fs23.rmSync)(stageRoot, { recursive: true, force: true });
57401
57642
  }
57402
57643
  broadcast({ type: "apps-changed", id });
57403
57644
  return { ok: true, id };
@@ -57411,24 +57652,24 @@ async function startServer() {
57411
57652
  }
57412
57653
  const inputs = appData.inputs.map((i) => ({ name: i.name, type: i.type }));
57413
57654
  const baked = bakeFloSource(appData.source.text, inputs);
57414
- const tmpRoot = (0, import_node_fs22.mkdtempSync)((0, import_node_path21.join)((0, import_node_os15.tmpdir)(), "floless-bake-"));
57415
- const backupDir = (0, import_node_path21.join)(tmpRoot, `${id}-backup`);
57416
- const bakeDir = (0, import_node_path21.join)(tmpRoot, id);
57417
- (0, import_node_fs22.cpSync)(appDir(id), backupDir, { recursive: true });
57418
- (0, import_node_fs22.cpSync)(appDir(id), bakeDir, { recursive: true });
57655
+ const tmpRoot = (0, import_node_fs23.mkdtempSync)((0, import_node_path22.join)((0, import_node_os16.tmpdir)(), "floless-bake-"));
57656
+ const backupDir = (0, import_node_path22.join)(tmpRoot, `${id}-backup`);
57657
+ const bakeDir = (0, import_node_path22.join)(tmpRoot, id);
57658
+ (0, import_node_fs23.cpSync)(appDir(id), backupDir, { recursive: true });
57659
+ (0, import_node_fs23.cpSync)(appDir(id), bakeDir, { recursive: true });
57419
57660
  const floName = appData.source.path.split(/[\\/]/).pop();
57420
- (0, import_node_fs22.writeFileSync)((0, import_node_path21.join)(bakeDir, floName), baked);
57421
- let appInstalled = true;
57661
+ (0, import_node_fs23.writeFileSync)((0, import_node_path22.join)(bakeDir, floName), baked);
57662
+ let appInstalled2 = true;
57422
57663
  try {
57423
57664
  await aware.uninstall(id);
57424
- appInstalled = false;
57665
+ appInstalled2 = false;
57425
57666
  try {
57426
57667
  await aware.install(bakeDir);
57427
- appInstalled = true;
57668
+ appInstalled2 = true;
57428
57669
  } catch (installErr) {
57429
57670
  try {
57430
57671
  await aware.install(backupDir);
57431
- appInstalled = true;
57672
+ appInstalled2 = true;
57432
57673
  } catch (restoreErr) {
57433
57674
  app.log.error({ id, tmpRoot, restoreErr: String(restoreErr) }, "bake: install AND restore failed \u2014 app uninstalled; backup retained");
57434
57675
  return reply.status(502).send({
@@ -57439,17 +57680,17 @@ async function startServer() {
57439
57680
  throw installErr;
57440
57681
  }
57441
57682
  try {
57442
- await aware.compile((0, import_node_path21.join)(appDir(id), floName));
57683
+ await aware.compile((0, import_node_path22.join)(appDir(id), floName));
57443
57684
  } catch (compileErr) {
57444
57685
  app.log.warn({ id, compileErr: String(compileErr) }, "bake: post-install recompile failed (app baked but may need a manual Compile)");
57445
57686
  }
57446
57687
  broadcast({ type: "baked", id });
57447
57688
  return { ok: true, id, agent: id, inputs };
57448
57689
  } finally {
57449
- if (appInstalled) (0, import_node_fs22.rmSync)(tmpRoot, { recursive: true, force: true });
57690
+ if (appInstalled2) (0, import_node_fs23.rmSync)(tmpRoot, { recursive: true, force: true });
57450
57691
  }
57451
57692
  });
57452
- const graftAgentsDir = () => (0, import_node_path21.join)((0, import_node_os15.homedir)(), ".aware", "agents");
57693
+ const graftAgentsDir = () => (0, import_node_path22.join)((0, import_node_os16.homedir)(), ".aware", "agents");
57453
57694
  app.post("/api/graft/match", async (req, reply) => {
57454
57695
  const { glob } = req.body ?? {};
57455
57696
  if (!glob) return reply.status(400).send({ ok: false, error: "glob required" });
@@ -57466,7 +57707,7 @@ async function startServer() {
57466
57707
  if (!sourceKind || !sourceRef) {
57467
57708
  return reply.status(400).send({ ok: false, error: "sourceKind and sourceRef required" });
57468
57709
  }
57469
- const tempHome = (0, import_node_fs22.mkdtempSync)((0, import_node_path21.join)((0, import_node_os15.tmpdir)(), "floless-graft-"));
57710
+ const tempHome = (0, import_node_fs23.mkdtempSync)((0, import_node_path22.join)((0, import_node_os16.tmpdir)(), "floless-graft-"));
57470
57711
  let result;
57471
57712
  try {
57472
57713
  result = await aware.build({
@@ -57479,19 +57720,19 @@ async function startServer() {
57479
57720
  awareHome: tempHome
57480
57721
  });
57481
57722
  } catch (err) {
57482
- (0, import_node_fs22.rmSync)(tempHome, { recursive: true, force: true });
57723
+ (0, import_node_fs23.rmSync)(tempHome, { recursive: true, force: true });
57483
57724
  const msg = err instanceof AwareError ? err.message : String(err?.message ?? err);
57484
57725
  return reply.status(422).send({ ok: false, error: msg });
57485
57726
  }
57486
57727
  const manifest = readStagedManifest(result.agentDir);
57487
57728
  if (!manifest) {
57488
- (0, import_node_fs22.rmSync)(tempHome, { recursive: true, force: true });
57729
+ (0, import_node_fs23.rmSync)(tempHome, { recursive: true, force: true });
57489
57730
  return reply.status(502).send({ ok: false, error: `build produced output at ${result.agentDir} but no manifest.yaml` });
57490
57731
  }
57491
57732
  const token = (0, import_node_crypto7.randomUUID)();
57492
57733
  registerStage(token, tempHome, result.agentId);
57493
57734
  const preview = buildPreview(manifest, sourceKind, sourceRef, token);
57494
- if ((0, import_node_fs22.existsSync)((0, import_node_path21.join)(graftAgentsDir(), result.agentId))) {
57735
+ if ((0, import_node_fs23.existsSync)((0, import_node_path22.join)(graftAgentsDir(), result.agentId))) {
57495
57736
  preview.warnings.unshift(`An agent named "${result.agentId}" is already installed \u2014 creating it will overwrite it.`);
57496
57737
  }
57497
57738
  return { ok: true, preview };
@@ -57510,7 +57751,7 @@ async function startServer() {
57510
57751
  registerStage(stagedRef, stage.tempDir, stage.agentId);
57511
57752
  return reply.status(409).send({ ok: false, error: err.message, agentId: stage.agentId, collision: true });
57512
57753
  }
57513
- (0, import_node_fs22.rmSync)(stage.tempDir, { recursive: true, force: true });
57754
+ (0, import_node_fs23.rmSync)(stage.tempDir, { recursive: true, force: true });
57514
57755
  throw err;
57515
57756
  }
57516
57757
  broadcast({ type: "grafted", id: stage.agentId });
@@ -57733,11 +57974,11 @@ async function startServer() {
57733
57974
  app.get("/api/requests/:id/snapshot/:n", async (req, reply) => {
57734
57975
  const n = Number.parseInt(req.params.n, 10);
57735
57976
  const p = Number.isInteger(n) ? snapshotPathFor(req.params.id, n) : null;
57736
- if (!p || !(0, import_node_fs22.existsSync)(p)) return reply.status(404).send({ ok: false, error: "snapshot not found" });
57977
+ if (!p || !(0, import_node_fs23.existsSync)(p)) return reply.status(404).send({ ok: false, error: "snapshot not found" });
57737
57978
  const ext = p.split(".").pop().toLowerCase();
57738
57979
  reply.header("Content-Type", ext === "png" ? "image/png" : ext === "webp" ? "image/webp" : "image/jpeg");
57739
57980
  reply.header("Cache-Control", "no-store");
57740
- return (0, import_node_fs22.readFileSync)(p);
57981
+ return (0, import_node_fs23.readFileSync)(p);
57741
57982
  });
57742
57983
  app.post(
57743
57984
  "/api/tweak",
@@ -57867,7 +58108,7 @@ async function startServer() {
57867
58108
  warn(`last-run-status:${ref} \u2014 trace exists but couldn't be parsed (corrupt/truncated)`);
57868
58109
  let finishedAt2 = null;
57869
58110
  try {
57870
- finishedAt2 = (0, import_node_fs22.statSync)(latest.path).mtime.toISOString();
58111
+ finishedAt2 = (0, import_node_fs23.statSync)(latest.path).mtime.toISOString();
57871
58112
  } catch {
57872
58113
  finishedAt2 = null;
57873
58114
  }
@@ -57877,7 +58118,7 @@ async function startServer() {
57877
58118
  let finishedAt = typeof runEnd?.ts === "string" ? runEnd.ts : null;
57878
58119
  if (!finishedAt) {
57879
58120
  try {
57880
- finishedAt = (0, import_node_fs22.statSync)(latest.path).mtime.toISOString();
58121
+ finishedAt = (0, import_node_fs23.statSync)(latest.path).mtime.toISOString();
57881
58122
  } catch {
57882
58123
  finishedAt = null;
57883
58124
  }