nstantpage-agent 0.5.34 → 0.6.1

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.
@@ -972,8 +972,8 @@ var require_command = __commonJS({
972
972
  "node_modules/commander/lib/command.js"(exports) {
973
973
  var EventEmitter = __require("node:events").EventEmitter;
974
974
  var childProcess2 = __require("node:child_process");
975
- var path17 = __require("node:path");
976
- var fs18 = __require("node:fs");
975
+ var path20 = __require("node:path");
976
+ var fs21 = __require("node:fs");
977
977
  var process15 = __require("node:process");
978
978
  var { Argument: Argument2, humanReadableArgName } = require_argument();
979
979
  var { CommanderError: CommanderError2 } = require_error();
@@ -1905,11 +1905,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
1905
1905
  let launchWithNode = false;
1906
1906
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
1907
1907
  function findFile(baseDir, baseName) {
1908
- const localBin = path17.resolve(baseDir, baseName);
1909
- if (fs18.existsSync(localBin)) return localBin;
1910
- if (sourceExt.includes(path17.extname(baseName))) return void 0;
1908
+ const localBin = path20.resolve(baseDir, baseName);
1909
+ if (fs21.existsSync(localBin)) return localBin;
1910
+ if (sourceExt.includes(path20.extname(baseName))) return void 0;
1911
1911
  const foundExt = sourceExt.find(
1912
- (ext) => fs18.existsSync(`${localBin}${ext}`)
1912
+ (ext) => fs21.existsSync(`${localBin}${ext}`)
1913
1913
  );
1914
1914
  if (foundExt) return `${localBin}${foundExt}`;
1915
1915
  return void 0;
@@ -1921,21 +1921,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
1921
1921
  if (this._scriptPath) {
1922
1922
  let resolvedScriptPath;
1923
1923
  try {
1924
- resolvedScriptPath = fs18.realpathSync(this._scriptPath);
1924
+ resolvedScriptPath = fs21.realpathSync(this._scriptPath);
1925
1925
  } catch (err) {
1926
1926
  resolvedScriptPath = this._scriptPath;
1927
1927
  }
1928
- executableDir = path17.resolve(
1929
- path17.dirname(resolvedScriptPath),
1928
+ executableDir = path20.resolve(
1929
+ path20.dirname(resolvedScriptPath),
1930
1930
  executableDir
1931
1931
  );
1932
1932
  }
1933
1933
  if (executableDir) {
1934
1934
  let localFile = findFile(executableDir, executableFile);
1935
1935
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
1936
- const legacyName = path17.basename(
1936
+ const legacyName = path20.basename(
1937
1937
  this._scriptPath,
1938
- path17.extname(this._scriptPath)
1938
+ path20.extname(this._scriptPath)
1939
1939
  );
1940
1940
  if (legacyName !== this._name) {
1941
1941
  localFile = findFile(
@@ -1946,7 +1946,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1946
1946
  }
1947
1947
  executableFile = localFile || executableFile;
1948
1948
  }
1949
- launchWithNode = sourceExt.includes(path17.extname(executableFile));
1949
+ launchWithNode = sourceExt.includes(path20.extname(executableFile));
1950
1950
  let proc;
1951
1951
  if (process15.platform !== "win32") {
1952
1952
  if (launchWithNode) {
@@ -2786,7 +2786,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2786
2786
  * @return {Command}
2787
2787
  */
2788
2788
  nameFromFilename(filename) {
2789
- this._name = path17.basename(filename, path17.extname(filename));
2789
+ this._name = path20.basename(filename, path20.extname(filename));
2790
2790
  return this;
2791
2791
  }
2792
2792
  /**
@@ -2800,9 +2800,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
2800
2800
  * @param {string} [path]
2801
2801
  * @return {(string|null|Command)}
2802
2802
  */
2803
- executableDir(path18) {
2804
- if (path18 === void 0) return this._executableDir;
2805
- this._executableDir = path18;
2803
+ executableDir(path21) {
2804
+ if (path21 === void 0) return this._executableDir;
2805
+ this._executableDir = path21;
2806
2806
  return this;
2807
2807
  }
2808
2808
  /**
@@ -3033,12 +3033,12 @@ var require_commander = __commonJS({
3033
3033
  });
3034
3034
 
3035
3035
  // node_modules/dot-prop/index.js
3036
- function getPathSegments(path17) {
3036
+ function getPathSegments(path20) {
3037
3037
  const parts = [];
3038
3038
  let currentSegment = "";
3039
3039
  let currentPart = "start";
3040
3040
  let isIgnoring = false;
3041
- for (const character of path17) {
3041
+ for (const character of path20) {
3042
3042
  switch (character) {
3043
3043
  case "\\": {
3044
3044
  if (currentPart === "index") {
@@ -3160,11 +3160,11 @@ function assertNotStringIndex(object, key) {
3160
3160
  throw new Error("Cannot use string index");
3161
3161
  }
3162
3162
  }
3163
- function getProperty(object, path17, value) {
3164
- if (!isObject(object) || typeof path17 !== "string") {
3163
+ function getProperty(object, path20, value) {
3164
+ if (!isObject(object) || typeof path20 !== "string") {
3165
3165
  return value === void 0 ? object : value;
3166
3166
  }
3167
- const pathArray = getPathSegments(path17);
3167
+ const pathArray = getPathSegments(path20);
3168
3168
  if (pathArray.length === 0) {
3169
3169
  return value;
3170
3170
  }
@@ -3184,12 +3184,12 @@ function getProperty(object, path17, value) {
3184
3184
  }
3185
3185
  return object === void 0 ? value : object;
3186
3186
  }
3187
- function setProperty(object, path17, value) {
3188
- if (!isObject(object) || typeof path17 !== "string") {
3187
+ function setProperty(object, path20, value) {
3188
+ if (!isObject(object) || typeof path20 !== "string") {
3189
3189
  return object;
3190
3190
  }
3191
3191
  const root = object;
3192
- const pathArray = getPathSegments(path17);
3192
+ const pathArray = getPathSegments(path20);
3193
3193
  for (let index = 0; index < pathArray.length; index++) {
3194
3194
  const key = pathArray[index];
3195
3195
  assertNotStringIndex(object, key);
@@ -3202,11 +3202,11 @@ function setProperty(object, path17, value) {
3202
3202
  }
3203
3203
  return root;
3204
3204
  }
3205
- function deleteProperty(object, path17) {
3206
- if (!isObject(object) || typeof path17 !== "string") {
3205
+ function deleteProperty(object, path20) {
3206
+ if (!isObject(object) || typeof path20 !== "string") {
3207
3207
  return false;
3208
3208
  }
3209
- const pathArray = getPathSegments(path17);
3209
+ const pathArray = getPathSegments(path20);
3210
3210
  for (let index = 0; index < pathArray.length; index++) {
3211
3211
  const key = pathArray[index];
3212
3212
  assertNotStringIndex(object, key);
@@ -3220,11 +3220,11 @@ function deleteProperty(object, path17) {
3220
3220
  }
3221
3221
  }
3222
3222
  }
3223
- function hasProperty(object, path17) {
3224
- if (!isObject(object) || typeof path17 !== "string") {
3223
+ function hasProperty(object, path20) {
3224
+ if (!isObject(object) || typeof path20 !== "string") {
3225
3225
  return false;
3226
3226
  }
3227
- const pathArray = getPathSegments(path17);
3227
+ const pathArray = getPathSegments(path20);
3228
3228
  if (pathArray.length === 0) {
3229
3229
  return false;
3230
3230
  }
@@ -7006,8 +7006,8 @@ var require_utils = __commonJS({
7006
7006
  }
7007
7007
  return ind;
7008
7008
  }
7009
- function removeDotSegments(path17) {
7010
- let input = path17;
7009
+ function removeDotSegments(path20) {
7010
+ let input = path20;
7011
7011
  const output = [];
7012
7012
  let nextSlash = -1;
7013
7013
  let len = 0;
@@ -7206,8 +7206,8 @@ var require_schemes = __commonJS({
7206
7206
  wsComponent.secure = void 0;
7207
7207
  }
7208
7208
  if (wsComponent.resourceName) {
7209
- const [path17, query] = wsComponent.resourceName.split("?");
7210
- wsComponent.path = path17 && path17 !== "/" ? path17 : void 0;
7209
+ const [path20, query] = wsComponent.resourceName.split("?");
7210
+ wsComponent.path = path20 && path20 !== "/" ? path20 : void 0;
7211
7211
  wsComponent.query = query;
7212
7212
  wsComponent.resourceName = void 0;
7213
7213
  }
@@ -11393,12 +11393,12 @@ var require_dist = __commonJS({
11393
11393
  throw new Error(`Unknown format "${name}"`);
11394
11394
  return f;
11395
11395
  };
11396
- function addFormats(ajv, list, fs18, exportName) {
11396
+ function addFormats(ajv, list, fs21, exportName) {
11397
11397
  var _a;
11398
11398
  var _b;
11399
11399
  (_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
11400
11400
  for (const f of list)
11401
- ajv.addFormat(f, fs18[f]);
11401
+ ajv.addFormat(f, fs21[f]);
11402
11402
  }
11403
11403
  module.exports = exports = formatsPlugin;
11404
11404
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -19294,15 +19294,15 @@ var require_pg_connection_string = __commonJS({
19294
19294
  if (config.sslcert || config.sslkey || config.sslrootcert || config.sslmode) {
19295
19295
  config.ssl = {};
19296
19296
  }
19297
- const fs18 = config.sslcert || config.sslkey || config.sslrootcert ? __require("fs") : null;
19297
+ const fs21 = config.sslcert || config.sslkey || config.sslrootcert ? __require("fs") : null;
19298
19298
  if (config.sslcert) {
19299
- config.ssl.cert = fs18.readFileSync(config.sslcert).toString();
19299
+ config.ssl.cert = fs21.readFileSync(config.sslcert).toString();
19300
19300
  }
19301
19301
  if (config.sslkey) {
19302
- config.ssl.key = fs18.readFileSync(config.sslkey).toString();
19302
+ config.ssl.key = fs21.readFileSync(config.sslkey).toString();
19303
19303
  }
19304
19304
  if (config.sslrootcert) {
19305
- config.ssl.ca = fs18.readFileSync(config.sslrootcert).toString();
19305
+ config.ssl.ca = fs21.readFileSync(config.sslrootcert).toString();
19306
19306
  }
19307
19307
  if (options.useLibpqCompat && config.uselibpqcompat) {
19308
19308
  throw new Error("Both useLibpqCompat and uselibpqcompat are set. Please use only one of them.");
@@ -21067,7 +21067,7 @@ var require_split2 = __commonJS({
21067
21067
  var require_helper = __commonJS({
21068
21068
  "node_modules/pgpass/lib/helper.js"(exports, module) {
21069
21069
  "use strict";
21070
- var path17 = __require("path");
21070
+ var path20 = __require("path");
21071
21071
  var Stream = __require("stream").Stream;
21072
21072
  var split = require_split2();
21073
21073
  var util = __require("util");
@@ -21106,7 +21106,7 @@ var require_helper = __commonJS({
21106
21106
  };
21107
21107
  module.exports.getFileName = function(rawEnv) {
21108
21108
  var env3 = rawEnv || process.env;
21109
- var file = env3.PGPASSFILE || (isWin ? path17.join(env3.APPDATA || "./", "postgresql", "pgpass.conf") : path17.join(env3.HOME || "./", ".pgpass"));
21109
+ var file = env3.PGPASSFILE || (isWin ? path20.join(env3.APPDATA || "./", "postgresql", "pgpass.conf") : path20.join(env3.HOME || "./", ".pgpass"));
21110
21110
  return file;
21111
21111
  };
21112
21112
  module.exports.usePgPass = function(stats, fname) {
@@ -21238,16 +21238,16 @@ var require_helper = __commonJS({
21238
21238
  var require_lib = __commonJS({
21239
21239
  "node_modules/pgpass/lib/index.js"(exports, module) {
21240
21240
  "use strict";
21241
- var path17 = __require("path");
21242
- var fs18 = __require("fs");
21241
+ var path20 = __require("path");
21242
+ var fs21 = __require("fs");
21243
21243
  var helper = require_helper();
21244
21244
  module.exports = function(connInfo, cb) {
21245
21245
  var file = helper.getFileName();
21246
- fs18.stat(file, function(err, stat) {
21246
+ fs21.stat(file, function(err, stat) {
21247
21247
  if (err || !helper.usePgPass(stat, file)) {
21248
21248
  return cb(void 0);
21249
21249
  }
21250
- var st = fs18.createReadStream(file);
21250
+ var st = fs21.createReadStream(file);
21251
21251
  helper.getPassword(connInfo, st, cb);
21252
21252
  });
21253
21253
  };
@@ -23989,8 +23989,8 @@ async function logoutCommand() {
23989
23989
 
23990
23990
  // dist/commands/start.js
23991
23991
  init_config();
23992
- import path15 from "path";
23993
- import fs16 from "fs";
23992
+ import path16 from "path";
23993
+ import fs17 from "fs";
23994
23994
  import os10 from "os";
23995
23995
  import { execSync as execSync2 } from "child_process";
23996
23996
 
@@ -24009,8 +24009,8 @@ init_config();
24009
24009
 
24010
24010
  // dist/localServer.js
24011
24011
  import http3 from "http";
24012
- import fs14 from "fs";
24013
- import path13 from "path";
24012
+ import fs15 from "fs";
24013
+ import path14 from "path";
24014
24014
  import os7 from "os";
24015
24015
  import { createRequire } from "module";
24016
24016
  import { spawn as spawn4 } from "child_process";
@@ -24426,6 +24426,48 @@ var FileManager = class {
24426
24426
  getProjectDir() {
24427
24427
  return this.projectDir;
24428
24428
  }
24429
+ /**
24430
+ * Build a recursive file tree of the project directory.
24431
+ * Returns a structure matching the backend's /api/projects/{id}/tree format.
24432
+ */
24433
+ async getFileTree() {
24434
+ const root = {
24435
+ name: path8.basename(this.projectDir),
24436
+ path: "",
24437
+ isDirectory: true,
24438
+ children: []
24439
+ };
24440
+ if (existsSync(this.projectDir)) {
24441
+ root.children = await this.buildTree(this.projectDir, "");
24442
+ }
24443
+ return root;
24444
+ }
24445
+ async buildTree(dir, relativePath) {
24446
+ const SKIP_DIRS3 = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", ".vite-cache", ".next", "__pycache__", ".turbo", ".cache"]);
24447
+ const entries = await fs10.readdir(dir, { withFileTypes: true });
24448
+ const result = [];
24449
+ const sorted = entries.sort((a, b) => {
24450
+ if (a.isDirectory() && !b.isDirectory())
24451
+ return -1;
24452
+ if (!a.isDirectory() && b.isDirectory())
24453
+ return 1;
24454
+ return a.name.localeCompare(b.name);
24455
+ });
24456
+ for (const entry of sorted) {
24457
+ if (entry.name.startsWith(".") && entry.name !== ".env")
24458
+ continue;
24459
+ const entryRelPath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
24460
+ if (entry.isDirectory()) {
24461
+ if (SKIP_DIRS3.has(entry.name))
24462
+ continue;
24463
+ const children = await this.buildTree(path8.join(dir, entry.name), entryRelPath);
24464
+ result.push({ name: entry.name, path: entryRelPath, isDirectory: true, children });
24465
+ } else {
24466
+ result.push({ name: entry.name, path: entryRelPath, isDirectory: false, children: [] });
24467
+ }
24468
+ }
24469
+ return result;
24470
+ }
24429
24471
  /**
24430
24472
  * Check if a path exists.
24431
24473
  */
@@ -24774,7 +24816,16 @@ var PackageInstaller = class {
24774
24816
  console.log(` [Installer] Installing project dependencies...`);
24775
24817
  const pm = this.detectPackageManager();
24776
24818
  const args = this.buildFastInstallArgs(pm);
24777
- await this.runCommand(pm, args, 3e5);
24819
+ try {
24820
+ await this.runCommand(pm, args, 3e5);
24821
+ } catch (err) {
24822
+ if (String(err.message || err).includes("ERR_PNPM_OUTDATED_LOCKFILE") || String(err.message || err).includes("frozen-lockfile")) {
24823
+ console.log(` [Installer] Lockfile outdated \u2014 retrying without frozen-lockfile...`);
24824
+ await this.runCommand(pm, ["install", "--no-frozen-lockfile"], 3e5);
24825
+ } else {
24826
+ throw err;
24827
+ }
24828
+ }
24778
24829
  this.writeDepsHash();
24779
24830
  console.log(` [Installer] Dependencies installed`);
24780
24831
  } finally {
@@ -25694,11 +25745,40 @@ var AgentSync = class {
25694
25745
  }
25695
25746
  };
25696
25747
 
25748
+ // dist/version.js
25749
+ import fs14 from "fs";
25750
+ import path13 from "path";
25751
+ import { fileURLToPath as fileURLToPath2 } from "url";
25752
+ var __filename_esm = fileURLToPath2(import.meta.url);
25753
+ var __dirname_esm = path13.dirname(__filename_esm);
25754
+ var _version = null;
25755
+ function getPackageVersion() {
25756
+ if (_version)
25757
+ return _version;
25758
+ let dir = __dirname_esm;
25759
+ for (let i = 0; i < 5; i++) {
25760
+ const pkgPath = path13.join(dir, "package.json");
25761
+ if (fs14.existsSync(pkgPath)) {
25762
+ try {
25763
+ const pkg = JSON.parse(fs14.readFileSync(pkgPath, "utf-8"));
25764
+ if (pkg.name === "nstantpage-agent") {
25765
+ _version = pkg.version;
25766
+ return _version;
25767
+ }
25768
+ } catch {
25769
+ }
25770
+ }
25771
+ dir = path13.dirname(dir);
25772
+ }
25773
+ _version = "0.0.0";
25774
+ return _version;
25775
+ }
25776
+
25697
25777
  // dist/localServer.js
25698
25778
  var ptyModule = null;
25699
25779
  try {
25700
- const _require = createRequire(import.meta.url);
25701
- ptyModule = _require("node-pty");
25780
+ const _require2 = createRequire(import.meta.url);
25781
+ ptyModule = _require2("node-pty");
25702
25782
  } catch {
25703
25783
  }
25704
25784
  var terminalSessions = /* @__PURE__ */ new Map();
@@ -25986,7 +26066,7 @@ Access-Control-Allow-Origin: *\r
25986
26066
  res.end(JSON.stringify({ error: `Unknown endpoint: ${pathOnly}` }));
25987
26067
  }
25988
26068
  }
25989
- getHandler(path17) {
26069
+ getHandler(path20) {
25990
26070
  const handlers = {
25991
26071
  "/live/sync": this.handleSync,
25992
26072
  "/live/files": this.handleFiles,
@@ -26033,12 +26113,15 @@ Access-Control-Allow-Origin: *\r
26033
26113
  "/live/push-to-backend": this.handlePushToBackend,
26034
26114
  "/live/pull-from-backend": this.handlePullFromBackend,
26035
26115
  "/live/disk-file": this.handleDiskFile,
26116
+ "/live/tree": this.handleTree,
26117
+ "/live/file-content": this.handleFileContent,
26118
+ "/live/save-file": this.handleSaveFile,
26036
26119
  "/health": this.handleHealth
26037
26120
  };
26038
- if (handlers[path17])
26039
- return handlers[path17];
26121
+ if (handlers[path20])
26122
+ return handlers[path20];
26040
26123
  for (const [route, handler] of Object.entries(handlers)) {
26041
- if (path17.startsWith(route + "/"))
26124
+ if (path20.startsWith(route + "/"))
26042
26125
  return handler;
26043
26126
  }
26044
26127
  return null;
@@ -26260,7 +26343,7 @@ Access-Control-Allow-Origin: *\r
26260
26343
  const label = parsed.label || `Terminal ${sessionCounter}`;
26261
26344
  let shell = null;
26262
26345
  let ptyProcess = null;
26263
- const spawnCwd = fs14.existsSync(this.options.projectDir) ? this.options.projectDir : process.cwd();
26346
+ const spawnCwd = fs15.existsSync(this.options.projectDir) ? this.options.projectDir : process.cwd();
26264
26347
  if (ptyModule) {
26265
26348
  try {
26266
26349
  ptyProcess = ptyModule.spawn(shellCmd, [], {
@@ -26422,7 +26505,7 @@ Access-Control-Allow-Origin: *\r
26422
26505
  connected: true,
26423
26506
  projectId: this.options.projectId,
26424
26507
  agent: {
26425
- version: "0.5.11",
26508
+ version: getPackageVersion(),
26426
26509
  hostname: os7.hostname(),
26427
26510
  platform: `${os7.platform()} ${os7.arch()}`
26428
26511
  }
@@ -26684,7 +26767,7 @@ Access-Control-Allow-Origin: *\r
26684
26767
  const projectDir = this.options.projectDir;
26685
26768
  console.log(` [LocalServer] Running production build for project ${this.options.projectId}...`);
26686
26769
  try {
26687
- const hasBuildScript = fs14.existsSync(path13.join(projectDir, "script", "build.ts"));
26770
+ const hasBuildScript = fs15.existsSync(path14.join(projectDir, "script", "build.ts"));
26688
26771
  const buildCmd = hasBuildScript ? "npx tsx script/build.ts" : "npx vite build";
26689
26772
  const buildResult = await new Promise((resolve) => {
26690
26773
  const proc = spawn4("sh", ["-c", buildCmd], {
@@ -26708,32 +26791,32 @@ Access-Control-Allow-Origin: *\r
26708
26791
  this.json(res, { success: false, error: errorMsg });
26709
26792
  return;
26710
26793
  }
26711
- const distDir = path13.join(projectDir, "dist");
26712
- if (!fs14.existsSync(distDir)) {
26794
+ const distDir = path14.join(projectDir, "dist");
26795
+ if (!fs15.existsSync(distDir)) {
26713
26796
  this.json(res, { success: false, error: "dist/ not found after build" });
26714
26797
  return;
26715
26798
  }
26716
26799
  const files = {};
26717
26800
  const collectFiles = (dir, prefix) => {
26718
- const entries = fs14.readdirSync(dir, { withFileTypes: true });
26801
+ const entries = fs15.readdirSync(dir, { withFileTypes: true });
26719
26802
  for (const entry of entries) {
26720
- const fullPath = path13.join(dir, entry.name);
26803
+ const fullPath = path14.join(dir, entry.name);
26721
26804
  const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
26722
26805
  if (entry.isDirectory()) {
26723
26806
  collectFiles(fullPath, relPath);
26724
26807
  } else {
26725
- const ext = path13.extname(entry.name).toLowerCase();
26808
+ const ext = path14.extname(entry.name).toLowerCase();
26726
26809
  const isBinary = [".png", ".jpg", ".jpeg", ".gif", ".webp", ".ico", ".woff", ".woff2", ".ttf", ".eot", ".mp3", ".mp4", ".ogg", ".wav"].includes(ext);
26727
26810
  if (isBinary) {
26728
- files[relPath] = "base64:" + fs14.readFileSync(fullPath).toString("base64");
26811
+ files[relPath] = "base64:" + fs15.readFileSync(fullPath).toString("base64");
26729
26812
  } else {
26730
- files[relPath] = fs14.readFileSync(fullPath, "utf-8");
26813
+ files[relPath] = fs15.readFileSync(fullPath, "utf-8");
26731
26814
  }
26732
26815
  }
26733
26816
  }
26734
26817
  };
26735
26818
  collectFiles(distDir, "");
26736
- const hasBackend = fs14.existsSync(path13.join(projectDir, "server", "index.ts")) || fs14.existsSync(path13.join(projectDir, "server", "index.js"));
26819
+ const hasBackend = fs15.existsSync(path14.join(projectDir, "server", "index.ts")) || fs15.existsSync(path14.join(projectDir, "server", "index.js"));
26737
26820
  console.log(` [LocalServer] Build completed: ${Object.keys(files).length} files (backend=${hasBackend})`);
26738
26821
  this.json(res, {
26739
26822
  success: true,
@@ -26834,6 +26917,47 @@ Access-Control-Allow-Origin: *\r
26834
26917
  this.json(res, { success: false, error: error.message }, 500);
26835
26918
  }
26836
26919
  }
26920
+ // ─── /live/tree ──────────────────────────────────────────────
26921
+ async handleTree(_req, res) {
26922
+ try {
26923
+ const tree = await this.fileManager.getFileTree();
26924
+ this.json(res, tree);
26925
+ } catch (error) {
26926
+ this.json(res, { error: error.message }, 500);
26927
+ }
26928
+ }
26929
+ // ─── /live/file-content ──────────────────────────────────────
26930
+ async handleFileContent(_req, res, _body, url) {
26931
+ const filePath = url.searchParams.get("path");
26932
+ if (!filePath) {
26933
+ this.json(res, { error: "Missing path parameter" }, 400);
26934
+ return;
26935
+ }
26936
+ try {
26937
+ const content = await this.fileManager.readFile(filePath);
26938
+ if (content === null) {
26939
+ this.json(res, { error: "File not found" }, 404);
26940
+ return;
26941
+ }
26942
+ this.json(res, { content });
26943
+ } catch (error) {
26944
+ this.json(res, { error: error.message }, 500);
26945
+ }
26946
+ }
26947
+ // ─── /live/save-file ────────────────────────────────────────
26948
+ async handleSaveFile(_req, res, body) {
26949
+ try {
26950
+ const { filePath, content } = JSON.parse(body);
26951
+ if (!filePath || content === void 0) {
26952
+ this.json(res, { error: "filePath and content required" }, 400);
26953
+ return;
26954
+ }
26955
+ await this.fileManager.writeFiles({ [filePath]: content });
26956
+ this.json(res, { success: true, message: "File saved" });
26957
+ } catch (error) {
26958
+ this.json(res, { error: error.message }, 500);
26959
+ }
26960
+ }
26837
26961
  // ─── /health ─────────────────────────────────────────────────
26838
26962
  async handleHealth(_req, res) {
26839
26963
  this.json(res, {
@@ -27118,7 +27242,7 @@ var TunnelClient = class {
27118
27242
  this.emitStatus("connected");
27119
27243
  this.send({
27120
27244
  type: "agent-info",
27121
- version: "0.5.11",
27245
+ version: getPackageVersion(),
27122
27246
  hostname: os8.hostname(),
27123
27247
  platform: `${os8.platform()} ${os8.arch()}`,
27124
27248
  deviceId: getDeviceId(),
@@ -27563,11 +27687,21 @@ var TunnelClient = class {
27563
27687
 
27564
27688
  // dist/statusServer.js
27565
27689
  import http5 from "http";
27566
- import fs15 from "fs";
27567
- import path14 from "path";
27690
+ import fs16 from "fs";
27691
+ import path15 from "path";
27568
27692
  import os9 from "os";
27693
+ import { spawn as spawn5 } from "child_process";
27694
+ import { createRequire as createRequire2 } from "module";
27569
27695
  var STATUS_PORT = 18999;
27570
- var STATUS_FILE = path14.join(os9.homedir(), ".nstantpage", "status.json");
27696
+ var STATUS_FILE = path15.join(os9.homedir(), ".nstantpage", "status.json");
27697
+ var _require = createRequire2(import.meta.url);
27698
+ var ptyModule2 = null;
27699
+ try {
27700
+ ptyModule2 = _require("node-pty");
27701
+ } catch {
27702
+ }
27703
+ var repoTerminalSessions = /* @__PURE__ */ new Map();
27704
+ var repoSessionCounter = 0;
27571
27705
  var StatusServer = class {
27572
27706
  server = null;
27573
27707
  getStatus;
@@ -27582,7 +27716,8 @@ var StatusServer = class {
27582
27716
  return new Promise((resolve, reject) => {
27583
27717
  this.server = http5.createServer((req, res) => {
27584
27718
  res.setHeader("Access-Control-Allow-Origin", "*");
27585
- res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
27719
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
27720
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, x-project-id");
27586
27721
  res.setHeader("Content-Type", "application/json");
27587
27722
  if (req.method === "OPTIONS") {
27588
27723
  res.writeHead(204);
@@ -27642,9 +27777,549 @@ var StatusServer = class {
27642
27777
  });
27643
27778
  return;
27644
27779
  }
27780
+ const parsedUrl = new URL(req.url || "/", `http://localhost:${STATUS_PORT}`);
27781
+ if (req.method === "GET" && url === "/browse/tree") {
27782
+ const dir = parsedUrl.searchParams.get("dir");
27783
+ if (!dir || !fs16.existsSync(dir)) {
27784
+ res.writeHead(400);
27785
+ res.end(JSON.stringify({ error: "Missing or invalid dir parameter" }));
27786
+ return;
27787
+ }
27788
+ try {
27789
+ const tree = buildFileTree(dir, path15.basename(dir));
27790
+ res.writeHead(200);
27791
+ res.end(JSON.stringify(tree));
27792
+ } catch (err) {
27793
+ res.writeHead(500);
27794
+ res.end(JSON.stringify({ error: err.message }));
27795
+ }
27796
+ return;
27797
+ }
27798
+ if (req.method === "GET" && url === "/browse/file") {
27799
+ const dir = parsedUrl.searchParams.get("dir");
27800
+ const filePath = parsedUrl.searchParams.get("path");
27801
+ if (!dir || !filePath) {
27802
+ res.writeHead(400);
27803
+ res.end(JSON.stringify({ error: "dir and path parameters required" }));
27804
+ return;
27805
+ }
27806
+ const fullPath = path15.resolve(dir, filePath);
27807
+ if (!fullPath.startsWith(path15.resolve(dir))) {
27808
+ res.writeHead(403);
27809
+ res.end(JSON.stringify({ error: "Path traversal not allowed" }));
27810
+ return;
27811
+ }
27812
+ try {
27813
+ if (!fs16.existsSync(fullPath)) {
27814
+ res.writeHead(404);
27815
+ res.end(JSON.stringify({ error: "File not found" }));
27816
+ return;
27817
+ }
27818
+ const content = fs16.readFileSync(fullPath, "utf-8");
27819
+ res.writeHead(200);
27820
+ res.end(JSON.stringify({ content }));
27821
+ } catch (err) {
27822
+ res.writeHead(500);
27823
+ res.end(JSON.stringify({ error: err.message }));
27824
+ }
27825
+ return;
27826
+ }
27827
+ if (req.method === "POST" && url === "/browse/save") {
27828
+ let body = "";
27829
+ req.on("data", (chunk) => {
27830
+ body += chunk.toString();
27831
+ });
27832
+ req.on("end", () => {
27833
+ try {
27834
+ const { dir, filePath, content } = JSON.parse(body);
27835
+ if (!dir || !filePath || content === void 0) {
27836
+ res.writeHead(400);
27837
+ res.end(JSON.stringify({ error: "dir, filePath, and content required" }));
27838
+ return;
27839
+ }
27840
+ const fullPath = path15.resolve(dir, filePath);
27841
+ if (!fullPath.startsWith(path15.resolve(dir))) {
27842
+ res.writeHead(403);
27843
+ res.end(JSON.stringify({ error: "Path traversal not allowed" }));
27844
+ return;
27845
+ }
27846
+ const parentDir = path15.dirname(fullPath);
27847
+ if (!fs16.existsSync(parentDir))
27848
+ fs16.mkdirSync(parentDir, { recursive: true });
27849
+ fs16.writeFileSync(fullPath, content, "utf-8");
27850
+ res.writeHead(200);
27851
+ res.end(JSON.stringify({ success: true }));
27852
+ } catch (err) {
27853
+ res.writeHead(500);
27854
+ res.end(JSON.stringify({ error: err.message }));
27855
+ }
27856
+ });
27857
+ return;
27858
+ }
27859
+ if (req.method === "GET" && url === "/browse/list-files") {
27860
+ const dir = parsedUrl.searchParams.get("dir");
27861
+ if (!dir || !fs16.existsSync(dir)) {
27862
+ res.writeHead(400);
27863
+ res.end(JSON.stringify({ error: "Missing or invalid dir parameter" }));
27864
+ return;
27865
+ }
27866
+ try {
27867
+ const files = [];
27868
+ collectFilesRecursive(dir, dir, files);
27869
+ res.writeHead(200);
27870
+ res.end(JSON.stringify({ files }));
27871
+ } catch (err) {
27872
+ res.writeHead(500);
27873
+ res.end(JSON.stringify({ error: err.message }));
27874
+ }
27875
+ return;
27876
+ }
27877
+ if (req.method === "POST" && url === "/browse/search") {
27878
+ let body = "";
27879
+ req.on("data", (chunk) => {
27880
+ body += chunk.toString();
27881
+ });
27882
+ req.on("end", () => {
27883
+ try {
27884
+ const { dir, query, isRegex, filePattern } = JSON.parse(body);
27885
+ if (!dir || !query) {
27886
+ res.writeHead(400);
27887
+ res.end(JSON.stringify({ error: "dir and query required" }));
27888
+ return;
27889
+ }
27890
+ if (!fs16.existsSync(dir)) {
27891
+ res.writeHead(400);
27892
+ res.end(JSON.stringify({ error: "dir does not exist" }));
27893
+ return;
27894
+ }
27895
+ const files = [];
27896
+ collectFilesRecursive(dir, dir, files);
27897
+ const regex = isRegex ? new RegExp(query, "gi") : null;
27898
+ const results = [];
27899
+ const maxResults = 100;
27900
+ for (const f of files) {
27901
+ if (results.length >= maxResults)
27902
+ break;
27903
+ if (filePattern && !matchGlob(f.filePath, filePattern))
27904
+ continue;
27905
+ const fullPath = path15.resolve(dir, f.filePath);
27906
+ let content;
27907
+ try {
27908
+ content = fs16.readFileSync(fullPath, "utf-8");
27909
+ } catch {
27910
+ continue;
27911
+ }
27912
+ const lines = content.split("\n");
27913
+ for (let i = 0; i < lines.length && results.length < maxResults; i++) {
27914
+ const matched = regex ? regex.test(lines[i]) : lines[i].toLowerCase().includes(query.toLowerCase());
27915
+ if (regex)
27916
+ regex.lastIndex = 0;
27917
+ if (matched) {
27918
+ results.push({ filePath: f.filePath, line: i + 1, content: lines[i] });
27919
+ }
27920
+ }
27921
+ }
27922
+ res.writeHead(200);
27923
+ res.end(JSON.stringify({ results }));
27924
+ } catch (err) {
27925
+ res.writeHead(500);
27926
+ res.end(JSON.stringify({ error: err.message }));
27927
+ }
27928
+ });
27929
+ return;
27930
+ }
27931
+ if (req.method === "POST" && url === "/browse/delete") {
27932
+ let body = "";
27933
+ req.on("data", (chunk) => {
27934
+ body += chunk.toString();
27935
+ });
27936
+ req.on("end", () => {
27937
+ try {
27938
+ const { dir, filePath } = JSON.parse(body);
27939
+ if (!dir || !filePath) {
27940
+ res.writeHead(400);
27941
+ res.end(JSON.stringify({ error: "dir and filePath required" }));
27942
+ return;
27943
+ }
27944
+ const fullPath = path15.resolve(dir, filePath);
27945
+ if (!fullPath.startsWith(path15.resolve(dir))) {
27946
+ res.writeHead(403);
27947
+ res.end(JSON.stringify({ error: "Path traversal not allowed" }));
27948
+ return;
27949
+ }
27950
+ if (!fs16.existsSync(fullPath)) {
27951
+ res.writeHead(404);
27952
+ res.end(JSON.stringify({ error: "File not found" }));
27953
+ return;
27954
+ }
27955
+ fs16.unlinkSync(fullPath);
27956
+ res.writeHead(200);
27957
+ res.end(JSON.stringify({ success: true }));
27958
+ } catch (err) {
27959
+ res.writeHead(500);
27960
+ res.end(JSON.stringify({ error: err.message }));
27961
+ }
27962
+ });
27963
+ return;
27964
+ }
27965
+ if (url === "/live/container-status") {
27966
+ res.writeHead(200);
27967
+ res.end(JSON.stringify({ status: "idle", projectId: parsedUrl.searchParams.get("projectId") || "repo" }));
27968
+ return;
27969
+ }
27970
+ if (req.method === "POST" && url === "/live/terminal") {
27971
+ let body = "";
27972
+ req.on("data", (chunk) => {
27973
+ body += chunk.toString();
27974
+ });
27975
+ req.on("end", () => {
27976
+ try {
27977
+ const parsed = body ? JSON.parse(body) : {};
27978
+ const command = parsed.command || "";
27979
+ const cwd = parsed.cwd || os9.homedir();
27980
+ const timeoutSeconds = Math.min(parsed.timeoutSeconds || 120, 600);
27981
+ const projectId = parsed.projectId || "repo";
27982
+ if (!command) {
27983
+ res.writeHead(400);
27984
+ res.end(JSON.stringify({ success: false, error: "command is required" }));
27985
+ return;
27986
+ }
27987
+ const spawnCwd = fs16.existsSync(cwd) ? cwd : os9.homedir();
27988
+ const shellCmd = process.platform === "win32" ? "cmd.exe" : process.env.SHELL || "/bin/bash";
27989
+ const shellEnv = { ...process.env, TERM: "xterm-256color", COLORTERM: "truecolor", FORCE_COLOR: "3" };
27990
+ const aiSession = Array.from(repoTerminalSessions.values()).find((s) => s.projectId === projectId && s.isAiSession && !s.exited);
27991
+ let stdout = "";
27992
+ let stderr = "";
27993
+ let finished = false;
27994
+ const child = spawn5(shellCmd, ["-c", command], {
27995
+ cwd: spawnCwd,
27996
+ env: shellEnv,
27997
+ stdio: ["pipe", "pipe", "pipe"]
27998
+ });
27999
+ child.stdout?.on("data", (d) => {
28000
+ const str = d.toString();
28001
+ stdout += str;
28002
+ if (aiSession) {
28003
+ for (const listener of aiSession.dataListeners) {
28004
+ try {
28005
+ listener(`\x1B[90m$ ${command}\x1B[0m
28006
+ `);
28007
+ } catch {
28008
+ }
28009
+ }
28010
+ for (const listener of aiSession.dataListeners) {
28011
+ try {
28012
+ listener(str);
28013
+ } catch {
28014
+ }
28015
+ }
28016
+ }
28017
+ });
28018
+ child.stderr?.on("data", (d) => {
28019
+ const str = d.toString();
28020
+ stderr += str;
28021
+ if (aiSession) {
28022
+ for (const listener of aiSession.dataListeners) {
28023
+ try {
28024
+ listener(str);
28025
+ } catch {
28026
+ }
28027
+ }
28028
+ }
28029
+ });
28030
+ const timeout = setTimeout(() => {
28031
+ if (!finished) {
28032
+ finished = true;
28033
+ try {
28034
+ child.kill("SIGKILL");
28035
+ } catch {
28036
+ }
28037
+ res.writeHead(200);
28038
+ res.end(JSON.stringify({
28039
+ success: false,
28040
+ exitCode: 124,
28041
+ stdout: stdout.slice(-5e4),
28042
+ stderr: stderr.slice(-5e4),
28043
+ error: `Timeout after ${timeoutSeconds}s`
28044
+ }));
28045
+ }
28046
+ }, timeoutSeconds * 1e3);
28047
+ child.on("exit", (code) => {
28048
+ if (finished)
28049
+ return;
28050
+ finished = true;
28051
+ clearTimeout(timeout);
28052
+ res.writeHead(200);
28053
+ res.end(JSON.stringify({
28054
+ success: code === 0,
28055
+ exitCode: code ?? -1,
28056
+ stdout: stdout.slice(-5e4),
28057
+ stderr: stderr.slice(-5e4),
28058
+ error: null
28059
+ }));
28060
+ });
28061
+ child.on("error", (err) => {
28062
+ if (finished)
28063
+ return;
28064
+ finished = true;
28065
+ clearTimeout(timeout);
28066
+ res.writeHead(200);
28067
+ res.end(JSON.stringify({
28068
+ success: false,
28069
+ exitCode: -1,
28070
+ stdout: "",
28071
+ stderr: "",
28072
+ error: err.message
28073
+ }));
28074
+ });
28075
+ } catch (err) {
28076
+ res.writeHead(500);
28077
+ res.end(JSON.stringify({ error: err.message }));
28078
+ }
28079
+ });
28080
+ return;
28081
+ }
28082
+ if (url === "/live/terminal/sessions") {
28083
+ if (req.method === "GET") {
28084
+ const pid = parsedUrl.searchParams.get("projectId") || "repo";
28085
+ const sessions = Array.from(repoTerminalSessions.values()).filter((s) => s.projectId === pid).map((s) => ({
28086
+ id: s.id,
28087
+ projectId: s.projectId,
28088
+ isAiSession: s.isAiSession,
28089
+ label: s.label,
28090
+ createdAt: s.createdAt,
28091
+ lastActivity: s.lastActivity,
28092
+ exited: s.exited,
28093
+ exitCode: s.exitCode,
28094
+ cols: s.cols,
28095
+ rows: s.rows
28096
+ }));
28097
+ res.writeHead(200);
28098
+ res.end(JSON.stringify({ success: true, sessions }));
28099
+ return;
28100
+ }
28101
+ if (req.method === "POST") {
28102
+ let body = "";
28103
+ req.on("data", (chunk) => {
28104
+ body += chunk.toString();
28105
+ });
28106
+ req.on("end", () => {
28107
+ try {
28108
+ const parsed = body ? JSON.parse(body) : {};
28109
+ const projectId = parsed.projectId || "repo";
28110
+ const cwd = parsed.cwd || os9.homedir();
28111
+ const cols = parsed.cols || 120;
28112
+ const rows = parsed.rows || 30;
28113
+ repoSessionCounter++;
28114
+ const sessionId = `repo-${Date.now()}-${repoSessionCounter}`;
28115
+ const label = parsed.label || `Terminal ${repoSessionCounter}`;
28116
+ const shellCmd = process.platform === "win32" ? "cmd.exe" : process.env.SHELL || "/bin/bash";
28117
+ const shellEnv = { ...process.env, TERM: "xterm-256color", COLORTERM: "truecolor", FORCE_COLOR: "3", COLUMNS: String(cols), LINES: String(rows) };
28118
+ const spawnCwd = fs16.existsSync(cwd) ? cwd : os9.homedir();
28119
+ let shell = null;
28120
+ let ptyProcess = null;
28121
+ if (ptyModule2) {
28122
+ try {
28123
+ ptyProcess = ptyModule2.spawn(shellCmd, [], { name: "xterm-256color", cols, rows, cwd: spawnCwd, env: shellEnv });
28124
+ } catch {
28125
+ ptyProcess = null;
28126
+ }
28127
+ }
28128
+ if (!ptyProcess) {
28129
+ try {
28130
+ shell = spawn5(shellCmd, ["-i"], { cwd: spawnCwd, env: shellEnv, stdio: ["pipe", "pipe", "pipe"] });
28131
+ } catch {
28132
+ shell = spawn5(shellCmd, [], { cwd: spawnCwd, env: shellEnv, stdio: ["pipe", "pipe", "pipe"] });
28133
+ }
28134
+ }
28135
+ const session = {
28136
+ id: sessionId,
28137
+ projectId,
28138
+ cwd: spawnCwd,
28139
+ shell,
28140
+ ptyProcess,
28141
+ outputBuffer: [],
28142
+ createdAt: Date.now(),
28143
+ lastActivity: Date.now(),
28144
+ cols,
28145
+ rows,
28146
+ isAiSession: parsed.isAiSession || false,
28147
+ label,
28148
+ exited: false,
28149
+ exitCode: null,
28150
+ dataListeners: /* @__PURE__ */ new Set(),
28151
+ exitListeners: /* @__PURE__ */ new Set()
28152
+ };
28153
+ const pushOutput = (str) => {
28154
+ session.outputBuffer.push(str);
28155
+ session.lastActivity = Date.now();
28156
+ while (session.outputBuffer.length > 500)
28157
+ session.outputBuffer.shift();
28158
+ for (const listener of session.dataListeners) {
28159
+ try {
28160
+ listener(str);
28161
+ } catch {
28162
+ }
28163
+ }
28164
+ };
28165
+ const handleExit = (code) => {
28166
+ session.exited = true;
28167
+ session.exitCode = code;
28168
+ for (const listener of session.exitListeners) {
28169
+ try {
28170
+ listener(code);
28171
+ } catch {
28172
+ }
28173
+ }
28174
+ };
28175
+ if (ptyProcess) {
28176
+ ptyProcess.onData((data) => pushOutput(data));
28177
+ ptyProcess.onExit(({ exitCode }) => handleExit(exitCode));
28178
+ } else if (shell) {
28179
+ shell.stdout?.on("data", (d) => pushOutput(d.toString()));
28180
+ shell.stderr?.on("data", (d) => pushOutput(d.toString()));
28181
+ shell.on("exit", (code) => handleExit(code));
28182
+ }
28183
+ repoTerminalSessions.set(sessionId, session);
28184
+ res.writeHead(200);
28185
+ res.end(JSON.stringify({
28186
+ success: true,
28187
+ session: { id: sessionId, projectId, isAiSession: session.isAiSession, label, createdAt: session.createdAt, lastActivity: session.lastActivity, exited: false, exitCode: null, cols, rows }
28188
+ }));
28189
+ } catch (err) {
28190
+ res.writeHead(500);
28191
+ res.end(JSON.stringify({ error: err.message }));
28192
+ }
28193
+ });
28194
+ return;
28195
+ }
28196
+ }
28197
+ if (req.method === "DELETE" && url?.startsWith("/live/terminal/sessions/")) {
28198
+ const sessionId = url.split("/").pop();
28199
+ const session = repoTerminalSessions.get(sessionId);
28200
+ if (session) {
28201
+ if (session.ptyProcess) {
28202
+ try {
28203
+ session.ptyProcess.kill();
28204
+ } catch {
28205
+ }
28206
+ }
28207
+ if (session.shell) {
28208
+ try {
28209
+ session.shell.kill();
28210
+ } catch {
28211
+ }
28212
+ }
28213
+ repoTerminalSessions.delete(sessionId);
28214
+ }
28215
+ res.writeHead(200);
28216
+ res.end(JSON.stringify({ success: true }));
28217
+ return;
28218
+ }
28219
+ if (req.method === "POST" && url === "/live/terminal/destroy") {
28220
+ let body = "";
28221
+ req.on("data", (chunk) => {
28222
+ body += chunk.toString();
28223
+ });
28224
+ req.on("end", () => {
28225
+ try {
28226
+ const { sessionId } = JSON.parse(body);
28227
+ const session = repoTerminalSessions.get(sessionId);
28228
+ if (session) {
28229
+ if (session.ptyProcess) {
28230
+ try {
28231
+ session.ptyProcess.kill();
28232
+ } catch {
28233
+ }
28234
+ }
28235
+ if (session.shell) {
28236
+ try {
28237
+ session.shell.kill();
28238
+ } catch {
28239
+ }
28240
+ }
28241
+ repoTerminalSessions.delete(sessionId);
28242
+ }
28243
+ res.writeHead(200);
28244
+ res.end(JSON.stringify({ success: true }));
28245
+ } catch (err) {
28246
+ res.writeHead(500);
28247
+ res.end(JSON.stringify({ error: err.message }));
28248
+ }
28249
+ });
28250
+ return;
28251
+ }
28252
+ if (req.headers.upgrade && req.headers.upgrade.toLowerCase() === "websocket") {
28253
+ return;
28254
+ }
27645
28255
  res.writeHead(404);
27646
28256
  res.end(JSON.stringify({ error: "Not found" }));
27647
28257
  });
28258
+ const wss = new import_websocket_server.default({ noServer: true });
28259
+ this.server.on("upgrade", (req, socket, head) => {
28260
+ const urlMatch = req.url?.match(/^\/live\/terminal\/ws\/([^/]+)\/([^/?]+)/);
28261
+ if (!urlMatch) {
28262
+ socket.destroy();
28263
+ return;
28264
+ }
28265
+ const sessionId = urlMatch[2];
28266
+ const session = repoTerminalSessions.get(sessionId);
28267
+ if (!session) {
28268
+ socket.destroy();
28269
+ return;
28270
+ }
28271
+ wss.handleUpgrade(req, socket, head, (ws) => {
28272
+ if (session.outputBuffer.length > 0) {
28273
+ ws.send(JSON.stringify({ type: "scrollback", data: session.outputBuffer.join("") }));
28274
+ }
28275
+ ws.send(JSON.stringify({ type: "connected", sessionId: session.id, projectId: session.projectId }));
28276
+ const dataListener = (data) => {
28277
+ try {
28278
+ if (ws.readyState === import_websocket.default.OPEN)
28279
+ ws.send(JSON.stringify({ type: "output", data }));
28280
+ } catch {
28281
+ }
28282
+ };
28283
+ const exitListener = (code) => {
28284
+ try {
28285
+ if (ws.readyState === import_websocket.default.OPEN)
28286
+ ws.send(JSON.stringify({ type: "exit", exitCode: code }));
28287
+ } catch {
28288
+ }
28289
+ };
28290
+ session.dataListeners.add(dataListener);
28291
+ session.exitListeners.add(exitListener);
28292
+ ws.on("message", (raw) => {
28293
+ try {
28294
+ const msg = JSON.parse(typeof raw === "string" ? raw : raw.toString());
28295
+ if (msg.type === "input" && msg.data) {
28296
+ if (session.ptyProcess)
28297
+ session.ptyProcess.write(msg.data);
28298
+ else if (session.shell?.stdin)
28299
+ session.shell.stdin.write(msg.data);
28300
+ } else if (msg.type === "resize" && msg.cols && msg.rows) {
28301
+ session.cols = msg.cols;
28302
+ session.rows = msg.rows;
28303
+ if (session.ptyProcess) {
28304
+ try {
28305
+ session.ptyProcess.resize(msg.cols, msg.rows);
28306
+ } catch {
28307
+ }
28308
+ }
28309
+ }
28310
+ } catch {
28311
+ }
28312
+ });
28313
+ ws.on("close", () => {
28314
+ session.dataListeners.delete(dataListener);
28315
+ session.exitListeners.delete(exitListener);
28316
+ });
28317
+ ws.on("error", () => {
28318
+ session.dataListeners.delete(dataListener);
28319
+ session.exitListeners.delete(exitListener);
28320
+ });
28321
+ });
28322
+ });
27648
28323
  this.server.on("error", (err) => {
27649
28324
  if (err.code === "EADDRINUSE") {
27650
28325
  console.log(` [Status] Port ${STATUS_PORT} in use \u2014 status server skipped`);
@@ -27653,7 +28328,7 @@ var StatusServer = class {
27653
28328
  }
27654
28329
  reject(err);
27655
28330
  });
27656
- this.server.listen(STATUS_PORT, "127.0.0.1", () => {
28331
+ this.server.listen(STATUS_PORT, () => {
27657
28332
  this.writeStatusFile();
27658
28333
  resolve();
27659
28334
  });
@@ -27665,25 +28340,134 @@ var StatusServer = class {
27665
28340
  this.server = null;
27666
28341
  }
27667
28342
  try {
27668
- if (fs15.existsSync(STATUS_FILE))
27669
- fs15.unlinkSync(STATUS_FILE);
28343
+ if (fs16.existsSync(STATUS_FILE))
28344
+ fs16.unlinkSync(STATUS_FILE);
27670
28345
  } catch {
27671
28346
  }
27672
28347
  }
27673
28348
  writeStatusFile() {
27674
- const dir = path14.dirname(STATUS_FILE);
27675
- if (!fs15.existsSync(dir))
27676
- fs15.mkdirSync(dir, { recursive: true });
27677
- fs15.writeFileSync(STATUS_FILE, JSON.stringify({
28349
+ const dir = path15.dirname(STATUS_FILE);
28350
+ if (!fs16.existsSync(dir))
28351
+ fs16.mkdirSync(dir, { recursive: true });
28352
+ fs16.writeFileSync(STATUS_FILE, JSON.stringify({
27678
28353
  port: STATUS_PORT,
27679
28354
  pid: process.pid,
27680
28355
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
27681
28356
  }, null, 2), "utf-8");
27682
28357
  }
27683
28358
  };
28359
+ var SKIP_DIRS2 = /* @__PURE__ */ new Set([
28360
+ "node_modules",
28361
+ "dist",
28362
+ ".git",
28363
+ ".vite-cache",
28364
+ ".next",
28365
+ "__pycache__",
28366
+ ".turbo",
28367
+ ".cache",
28368
+ "build",
28369
+ ".svelte-kit"
28370
+ ]);
28371
+ function buildFileTree(dirPath, name, relativePath = "") {
28372
+ const node = { name, path: relativePath, isDirectory: true, children: [] };
28373
+ let entries;
28374
+ try {
28375
+ entries = fs16.readdirSync(dirPath, { withFileTypes: true });
28376
+ } catch {
28377
+ return node;
28378
+ }
28379
+ const dirs = [];
28380
+ const files = [];
28381
+ for (const entry of entries) {
28382
+ if (entry.name.startsWith(".") && entry.name !== ".env")
28383
+ continue;
28384
+ const childRelative = relativePath ? `${relativePath}/${entry.name}` : entry.name;
28385
+ if (entry.isDirectory()) {
28386
+ if (SKIP_DIRS2.has(entry.name))
28387
+ continue;
28388
+ dirs.push(buildFileTree(path15.join(dirPath, entry.name), entry.name, childRelative));
28389
+ } else {
28390
+ files.push({ name: entry.name, path: childRelative, isDirectory: false, children: [] });
28391
+ }
28392
+ }
28393
+ dirs.sort((a, b) => a.name.localeCompare(b.name));
28394
+ files.sort((a, b) => a.name.localeCompare(b.name));
28395
+ node.children = [...dirs, ...files];
28396
+ return node;
28397
+ }
28398
+ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
28399
+ ".png",
28400
+ ".jpg",
28401
+ ".jpeg",
28402
+ ".gif",
28403
+ ".webp",
28404
+ ".ico",
28405
+ ".svg",
28406
+ ".bmp",
28407
+ ".woff",
28408
+ ".woff2",
28409
+ ".ttf",
28410
+ ".eot",
28411
+ ".otf",
28412
+ ".zip",
28413
+ ".tar",
28414
+ ".gz",
28415
+ ".rar",
28416
+ ".7z",
28417
+ ".pdf",
28418
+ ".doc",
28419
+ ".docx",
28420
+ ".xls",
28421
+ ".xlsx",
28422
+ ".mp3",
28423
+ ".mp4",
28424
+ ".wav",
28425
+ ".avi",
28426
+ ".mov",
28427
+ ".exe",
28428
+ ".dll",
28429
+ ".so",
28430
+ ".dylib",
28431
+ ".o"
28432
+ ]);
28433
+ function collectFilesRecursive(baseDir, currentDir, out) {
28434
+ let entries;
28435
+ try {
28436
+ entries = fs16.readdirSync(currentDir, { withFileTypes: true });
28437
+ } catch {
28438
+ return;
28439
+ }
28440
+ for (const entry of entries) {
28441
+ if (entry.name.startsWith(".") && entry.name !== ".env")
28442
+ continue;
28443
+ const fullPath = path15.join(currentDir, entry.name);
28444
+ const relativePath = path15.relative(baseDir, fullPath);
28445
+ if (entry.isDirectory()) {
28446
+ if (SKIP_DIRS2.has(entry.name))
28447
+ continue;
28448
+ collectFilesRecursive(baseDir, fullPath, out);
28449
+ } else {
28450
+ const ext = path15.extname(entry.name).toLowerCase();
28451
+ if (BINARY_EXTENSIONS.has(ext)) {
28452
+ out.push({ filePath: relativePath, totalLines: 0 });
28453
+ } else {
28454
+ try {
28455
+ const content = fs16.readFileSync(fullPath, "utf-8");
28456
+ out.push({ filePath: relativePath, totalLines: content.split("\n").length });
28457
+ } catch {
28458
+ out.push({ filePath: relativePath, totalLines: 0 });
28459
+ }
28460
+ }
28461
+ }
28462
+ }
28463
+ }
28464
+ function matchGlob(filePath, pattern) {
28465
+ const regexPattern = "^" + pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "<<<GLOBSTAR>>>").replace(/\*/g, "[^/]*").replace(/<<<GLOBSTAR>>>/g, ".*").replace(/\?/g, ".") + "$";
28466
+ return new RegExp(regexPattern, "i").test(filePath);
28467
+ }
27684
28468
 
27685
28469
  // dist/commands/start.js
27686
- var VERSION = "0.5.29";
28470
+ var VERSION = getPackageVersion();
27687
28471
  function resolveBackendUrl(options) {
27688
28472
  if (options.backend)
27689
28473
  return options.backend.replace(/\/$/, "");
@@ -27692,10 +28476,10 @@ function resolveBackendUrl(options) {
27692
28476
  }
27693
28477
  function resolveProjectDir(directory, projectId, customDir) {
27694
28478
  if (customDir)
27695
- return path15.resolve(customDir);
28479
+ return path16.resolve(customDir);
27696
28480
  if (directory !== ".")
27697
- return path15.resolve(directory);
27698
- const defaultBase = path15.join(os10.homedir(), ".nstantpage", "projects", projectId);
28481
+ return path16.resolve(directory);
28482
+ const defaultBase = path16.join(os10.homedir(), ".nstantpage", "projects", projectId);
27699
28483
  return defaultBase;
27700
28484
  }
27701
28485
  async function fetchProjectFiles(backendUrl, projectId, projectDir, token) {
@@ -27715,20 +28499,20 @@ async function fetchProjectFiles(backendUrl, projectId, projectDir, token) {
27715
28499
  if (!data.files || data.files.length === 0) {
27716
28500
  throw new Error("No files returned from backend. Is the project ID correct?");
27717
28501
  }
27718
- const versionFile = path15.join(projectDir, ".nstantpage-version");
27719
- const existingVersion = fs16.existsSync(versionFile) ? fs16.readFileSync(versionFile, "utf-8").trim() : null;
28502
+ const versionFile = path16.join(projectDir, ".nstantpage-version");
28503
+ const existingVersion = fs17.existsSync(versionFile) ? fs17.readFileSync(versionFile, "utf-8").trim() : null;
27720
28504
  if (existingVersion === String(data.versionId)) {
27721
28505
  console.log(source_default.gray(` Files up-to-date (version ${data.version})`));
27722
28506
  return { fileCount: data.files.length, isNew: false, versionId: String(data.versionId) };
27723
28507
  }
27724
28508
  const dirsToCreate = /* @__PURE__ */ new Set();
27725
28509
  for (const file of data.files) {
27726
- const filePath = path15.join(projectDir, file.path);
27727
- dirsToCreate.add(path15.dirname(filePath));
28510
+ const filePath = path16.join(projectDir, file.path);
28511
+ dirsToCreate.add(path16.dirname(filePath));
27728
28512
  }
27729
28513
  for (const dir of dirsToCreate) {
27730
- if (!fs16.existsSync(dir)) {
27731
- fs16.mkdirSync(dir, { recursive: true });
28514
+ if (!fs17.existsSync(dir)) {
28515
+ fs17.mkdirSync(dir, { recursive: true });
27732
28516
  }
27733
28517
  }
27734
28518
  const BATCH_SIZE = 50;
@@ -27736,12 +28520,12 @@ async function fetchProjectFiles(backendUrl, projectId, projectDir, token) {
27736
28520
  for (let i = 0; i < data.files.length; i += BATCH_SIZE) {
27737
28521
  const batch = data.files.slice(i, i + BATCH_SIZE);
27738
28522
  await Promise.all(batch.map(async (file) => {
27739
- const filePath = path15.join(projectDir, file.path);
27740
- await fs16.promises.writeFile(filePath, file.content, "utf-8");
28523
+ const filePath = path16.join(projectDir, file.path);
28524
+ await fs17.promises.writeFile(filePath, file.content, "utf-8");
27741
28525
  }));
27742
28526
  written += batch.length;
27743
28527
  }
27744
- fs16.writeFileSync(versionFile, String(data.versionId), "utf-8");
28528
+ fs17.writeFileSync(versionFile, String(data.versionId), "utf-8");
27745
28529
  console.log(source_default.green(` \u2713 ${written} files downloaded (version ${data.version})`));
27746
28530
  return { fileCount: written, isNew: true, versionId: String(data.versionId) };
27747
28531
  }
@@ -27786,6 +28570,11 @@ function cleanupPreviousAgent(projectId, apiPort, devPort) {
27786
28570
  killPort(apiPort);
27787
28571
  if (devPort !== oldDevPort)
27788
28572
  killPort(devPort);
28573
+ const backendPort = devPort + 1001;
28574
+ killPort(backendPort);
28575
+ const oldBackendPort = oldDevPort ? oldDevPort + 1001 : 0;
28576
+ if (oldBackendPort && oldBackendPort !== backendPort)
28577
+ killPort(oldBackendPort);
27789
28578
  clearProjectConfig(projectId);
27790
28579
  }
27791
28580
  async function startCommand(directory, options) {
@@ -27840,8 +28629,8 @@ async function startCommand(directory, options) {
27840
28629
  apiPort = allocated.apiPort;
27841
28630
  }
27842
28631
  const projectDir = resolveProjectDir(directory, projectId, options.dir);
27843
- if (!fs16.existsSync(projectDir)) {
27844
- fs16.mkdirSync(projectDir, { recursive: true });
28632
+ if (!fs17.existsSync(projectDir)) {
28633
+ fs17.mkdirSync(projectDir, { recursive: true });
27845
28634
  }
27846
28635
  conf.set("projectId", projectId);
27847
28636
  cleanupPreviousAgent(projectId, apiPort, devPort);
@@ -27863,7 +28652,7 @@ async function startCommand(directory, options) {
27863
28652
  const { fileCount, isNew, versionId } = await fetchProjectFiles(backendUrl, projectId, projectDir, token);
27864
28653
  fetchedVersionId = versionId;
27865
28654
  if (!installer.areDependenciesInstalled()) {
27866
- if (fs16.existsSync(path15.join(projectDir, "package.json"))) {
28655
+ if (fs17.existsSync(path16.join(projectDir, "package.json"))) {
27867
28656
  console.log(source_default.gray(" Installing dependencies..."));
27868
28657
  try {
27869
28658
  await installer.ensureDependencies();
@@ -27879,16 +28668,16 @@ async function startCommand(directory, options) {
27879
28668
  } catch (err) {
27880
28669
  console.log(source_default.yellow(` \u26A0 Could not fetch project files: ${err.message}`));
27881
28670
  console.log(source_default.gray(" Continuing with existing local files (if any)..."));
27882
- if (!fs16.existsSync(path15.join(projectDir, "package.json"))) {
28671
+ if (!fs17.existsSync(path16.join(projectDir, "package.json"))) {
27883
28672
  console.log(source_default.red(`
27884
28673
  \u2717 No package.json found and cannot fetch files from backend.`));
27885
28674
  console.log(source_default.gray(" Check your project ID and authentication."));
27886
28675
  process.exit(1);
27887
28676
  }
27888
28677
  }
27889
- const localConfigPath = path15.join(projectDir, ".nstantpage.json");
27890
- if (!fs16.existsSync(localConfigPath)) {
27891
- fs16.writeFileSync(localConfigPath, JSON.stringify({ projectId }, null, 2), "utf-8");
28678
+ const localConfigPath = path16.join(projectDir, ".nstantpage.json");
28679
+ if (!fs17.existsSync(localConfigPath)) {
28680
+ fs17.writeFileSync(localConfigPath, JSON.stringify({ projectId }, null, 2), "utf-8");
27892
28681
  }
27893
28682
  let databaseUrl = null;
27894
28683
  try {
@@ -28112,9 +28901,9 @@ async function startStandbyMode(token, options, backendUrl, deviceId) {
28112
28901
  await existing.localServer.stop();
28113
28902
  activeProjects.delete(pid);
28114
28903
  const projectDir = resolveProjectDir(".", pid);
28115
- if (fs16.existsSync(projectDir)) {
28904
+ if (fs17.existsSync(projectDir)) {
28116
28905
  console.log(source_default.gray(` Wiping project directory: ${projectDir}`));
28117
- fs16.rmSync(projectDir, { recursive: true, force: true });
28906
+ fs17.rmSync(projectDir, { recursive: true, force: true });
28118
28907
  }
28119
28908
  } else if (activeProjects.has(pid)) {
28120
28909
  console.log(source_default.yellow(` Project ${pid} is already running`));
@@ -28277,8 +29066,8 @@ async function startAdditionalProject(projectId, opts) {
28277
29066
  try {
28278
29067
  const allocated = allocatePortsForProject(projectId);
28279
29068
  const projectDir = resolveProjectDir(".", projectId);
28280
- if (!fs16.existsSync(projectDir))
28281
- fs16.mkdirSync(projectDir, { recursive: true });
29069
+ if (!fs17.existsSync(projectDir))
29070
+ fs17.mkdirSync(projectDir, { recursive: true });
28282
29071
  cleanupPreviousAgent(projectId, allocated.apiPort, allocated.devPort);
28283
29072
  await new Promise((r) => setTimeout(r, 50));
28284
29073
  progress("fetching-files", "Fetching project files...");
@@ -28351,7 +29140,7 @@ async function startAdditionalProject(projectId, opts) {
28351
29140
  localServer.markSynced(fetchedVersionId);
28352
29141
  }
28353
29142
  const installer = new PackageInstaller({ projectDir });
28354
- const needsInstall = !installer.areDependenciesInstalled() && fs16.existsSync(path15.join(projectDir, "package.json"));
29143
+ const needsInstall = !installer.areDependenciesInstalled() && fs17.existsSync(path16.join(projectDir, "package.json"));
28355
29144
  if (needsInstall) {
28356
29145
  const installStart = Date.now();
28357
29146
  console.log(source_default.gray(` Installing dependencies...`));
@@ -28462,13 +29251,13 @@ async function statusCommand() {
28462
29251
 
28463
29252
  // dist/commands/service.js
28464
29253
  init_config();
28465
- import fs17 from "fs";
28466
- import path16 from "path";
29254
+ import fs18 from "fs";
29255
+ import path17 from "path";
28467
29256
  import os11 from "os";
28468
29257
  import { execSync as execSync3 } from "child_process";
28469
- import { fileURLToPath as fileURLToPath2 } from "url";
28470
- var __filename_esm = fileURLToPath2(import.meta.url);
28471
- var __dirname_esm = path16.dirname(__filename_esm);
29258
+ import { fileURLToPath as fileURLToPath3 } from "url";
29259
+ var __filename_esm2 = fileURLToPath3(import.meta.url);
29260
+ var __dirname_esm2 = path17.dirname(__filename_esm2);
28472
29261
  var PLIST_LABEL = "com.nstantpage.agent";
28473
29262
  var SYSTEMD_SERVICE = "nstantpage-agent";
28474
29263
  var WIN_TASK_NAME = "NstantpageAgent";
@@ -28495,8 +29284,8 @@ async function serviceStopCommand() {
28495
29284
  const platform2 = os11.platform();
28496
29285
  let stopped = false;
28497
29286
  if (platform2 === "darwin") {
28498
- const plistPath = path16.join(os11.homedir(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
28499
- if (fs17.existsSync(plistPath)) {
29287
+ const plistPath = path17.join(os11.homedir(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
29288
+ if (fs18.existsSync(plistPath)) {
28500
29289
  try {
28501
29290
  execSync3(`launchctl unload "${plistPath}" 2>/dev/null`, { encoding: "utf-8" });
28502
29291
  console.log(source_default.green(" \u2713 Background service stopped"));
@@ -28557,7 +29346,7 @@ async function serviceInstallCommand(options = {}) {
28557
29346
  return;
28558
29347
  }
28559
29348
  if (electronAppPath === null) {
28560
- console.log(source_default.gray(" Tip: Install the nstantpage desktop app for a system tray icon."));
29349
+ console.log(source_default.gray(' Tip: Run "nstantpage update --desktop" to download the desktop app with tray icon.'));
28561
29350
  }
28562
29351
  if (platform2 === "darwin") {
28563
29352
  await installLaunchd(gateway, token);
@@ -28624,12 +29413,12 @@ async function serviceStatusCommand() {
28624
29413
  } else {
28625
29414
  console.log(source_default.gray(" Service status not available on this platform"));
28626
29415
  }
28627
- const logPath = path16.join(os11.homedir(), ".nstantpage", "agent.log");
28628
- if (fs17.existsSync(logPath)) {
28629
- const stats = fs17.statSync(logPath);
29416
+ const logPath = path17.join(os11.homedir(), ".nstantpage", "agent.log");
29417
+ if (fs18.existsSync(logPath)) {
29418
+ const stats = fs18.statSync(logPath);
28630
29419
  console.log(source_default.gray(` Log file: ${logPath} (${(stats.size / 1024).toFixed(1)}KB)`));
28631
29420
  try {
28632
- const content = fs17.readFileSync(logPath, "utf-8");
29421
+ const content = fs18.readFileSync(logPath, "utf-8");
28633
29422
  const lines = content.trim().split("\n").slice(-5);
28634
29423
  if (lines.length > 0) {
28635
29424
  console.log(source_default.gray(" Last log lines:"));
@@ -28643,35 +29432,39 @@ function findElectronApp() {
28643
29432
  const platform2 = os11.platform();
28644
29433
  if (platform2 === "darwin") {
28645
29434
  const candidates = [
29435
+ // Downloaded by postinstall (npm i -g nstantpage-agent)
29436
+ path17.join(os11.homedir(), ".nstantpage", "desktop", "nstantpage.app"),
28646
29437
  "/Applications/nstantpage.app",
28647
- path16.join(os11.homedir(), "Applications", "nstantpage.app"),
29438
+ path17.join(os11.homedir(), "Applications", "nstantpage.app"),
28648
29439
  // Dev build location (from electron-builder --dir)
28649
- path16.join(__dirname_esm, "..", "..", "tray", "dist", "mac-arm64", "nstantpage.app"),
28650
- path16.join(__dirname_esm, "..", "..", "tray", "dist", "mac", "nstantpage.app")
29440
+ path17.join(__dirname_esm2, "..", "..", "tray", "dist", "mac-arm64", "nstantpage.app"),
29441
+ path17.join(__dirname_esm2, "..", "..", "tray", "dist", "mac", "nstantpage.app")
28651
29442
  ];
28652
29443
  for (const p of candidates) {
28653
- if (fs17.existsSync(p))
29444
+ if (fs18.existsSync(p))
28654
29445
  return p;
28655
29446
  }
28656
29447
  } else if (platform2 === "win32") {
28657
29448
  const candidates = [
28658
- path16.join(os11.homedir(), "AppData", "Local", "Programs", "nstantpage", "nstantpage.exe"),
28659
- path16.join("C:\\Program Files", "nstantpage", "nstantpage.exe")
29449
+ // Downloaded by postinstall (npm i -g nstantpage-agent)
29450
+ path17.join(os11.homedir(), ".nstantpage", "desktop", "nstantpage.exe"),
29451
+ path17.join(os11.homedir(), "AppData", "Local", "Programs", "nstantpage", "nstantpage.exe"),
29452
+ path17.join("C:\\Program Files", "nstantpage", "nstantpage.exe")
28660
29453
  ];
28661
29454
  for (const p of candidates) {
28662
- if (fs17.existsSync(p))
29455
+ if (fs18.existsSync(p))
28663
29456
  return p;
28664
29457
  }
28665
29458
  }
28666
29459
  return null;
28667
29460
  }
28668
29461
  async function installElectronLaunchd(appPath) {
28669
- const plistDir = path16.join(os11.homedir(), "Library", "LaunchAgents");
28670
- const plistPath = path16.join(plistDir, `${PLIST_LABEL}.plist`);
28671
- const logPath = path16.join(os11.homedir(), ".nstantpage", "agent.log");
28672
- const errPath = path16.join(os11.homedir(), ".nstantpage", "agent.err.log");
28673
- fs17.mkdirSync(plistDir, { recursive: true });
28674
- fs17.mkdirSync(path16.dirname(logPath), { recursive: true });
29462
+ const plistDir = path17.join(os11.homedir(), "Library", "LaunchAgents");
29463
+ const plistPath = path17.join(plistDir, `${PLIST_LABEL}.plist`);
29464
+ const logPath = path17.join(os11.homedir(), ".nstantpage", "agent.log");
29465
+ const errPath = path17.join(os11.homedir(), ".nstantpage", "agent.err.log");
29466
+ fs18.mkdirSync(plistDir, { recursive: true });
29467
+ fs18.mkdirSync(path17.dirname(logPath), { recursive: true });
28675
29468
  const plist = `<?xml version="1.0" encoding="UTF-8"?>
28676
29469
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
28677
29470
  <plist version="1.0">
@@ -28706,7 +29499,7 @@ async function installElectronLaunchd(appPath) {
28706
29499
  <integer>10</integer>
28707
29500
  </dict>
28708
29501
  </plist>`;
28709
- fs17.writeFileSync(plistPath, plist, "utf-8");
29502
+ fs18.writeFileSync(plistPath, plist, "utf-8");
28710
29503
  try {
28711
29504
  execSync3(`launchctl unload "${plistPath}" 2>/dev/null`, { encoding: "utf-8" });
28712
29505
  } catch {
@@ -28723,8 +29516,8 @@ async function installElectronLaunchd(appPath) {
28723
29516
  console.log(source_default.blue(" Right-click the tray icon for options. Open nstantpage.com to connect projects.\n"));
28724
29517
  }
28725
29518
  async function installElectronWindowsTask(appPath) {
28726
- const logPath = path16.join(os11.homedir(), ".nstantpage", "agent.log");
28727
- fs17.mkdirSync(path16.dirname(logPath), { recursive: true });
29519
+ const logPath = path17.join(os11.homedir(), ".nstantpage", "agent.log");
29520
+ fs18.mkdirSync(path17.dirname(logPath), { recursive: true });
28728
29521
  try {
28729
29522
  execSync3(`schtasks /delete /tn "${WIN_TASK_NAME}" /f 2>nul`, { encoding: "utf-8" });
28730
29523
  } catch {
@@ -28753,8 +29546,8 @@ function getAgentBinPath() {
28753
29546
  }
28754
29547
  try {
28755
29548
  const npmRoot = execSync3("npm root -g", { encoding: "utf-8" }).trim();
28756
- const bin = path16.join(npmRoot, ".bin", "nstantpage");
28757
- if (fs17.existsSync(bin))
29549
+ const bin = path17.join(npmRoot, ".bin", "nstantpage");
29550
+ if (fs18.existsSync(bin))
28758
29551
  return bin;
28759
29552
  } catch {
28760
29553
  }
@@ -28770,12 +29563,12 @@ function getNodePath() {
28770
29563
  async function installLaunchd(gateway, token) {
28771
29564
  const binPath = getAgentBinPath();
28772
29565
  const nodePath = getNodePath();
28773
- const plistDir = path16.join(os11.homedir(), "Library", "LaunchAgents");
28774
- const plistPath = path16.join(plistDir, `${PLIST_LABEL}.plist`);
28775
- const logPath = path16.join(os11.homedir(), ".nstantpage", "agent.log");
28776
- const errPath = path16.join(os11.homedir(), ".nstantpage", "agent.err.log");
28777
- fs17.mkdirSync(plistDir, { recursive: true });
28778
- fs17.mkdirSync(path16.dirname(logPath), { recursive: true });
29566
+ const plistDir = path17.join(os11.homedir(), "Library", "LaunchAgents");
29567
+ const plistPath = path17.join(plistDir, `${PLIST_LABEL}.plist`);
29568
+ const logPath = path17.join(os11.homedir(), ".nstantpage", "agent.log");
29569
+ const errPath = path17.join(os11.homedir(), ".nstantpage", "agent.err.log");
29570
+ fs18.mkdirSync(plistDir, { recursive: true });
29571
+ fs18.mkdirSync(path17.dirname(logPath), { recursive: true });
28779
29572
  const plist = `<?xml version="1.0" encoding="UTF-8"?>
28780
29573
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
28781
29574
  <plist version="1.0">
@@ -28815,7 +29608,7 @@ async function installLaunchd(gateway, token) {
28815
29608
  <integer>-5</integer>
28816
29609
  </dict>
28817
29610
  </plist>`;
28818
- fs17.writeFileSync(plistPath, plist, "utf-8");
29611
+ fs18.writeFileSync(plistPath, plist, "utf-8");
28819
29612
  try {
28820
29613
  execSync3(`launchctl unload "${plistPath}" 2>/dev/null`, { encoding: "utf-8" });
28821
29614
  } catch {
@@ -28831,8 +29624,8 @@ async function installLaunchd(gateway, token) {
28831
29624
  console.log(source_default.blue(' Open any project on nstantpage.com and click "Connect" to use it.\n'));
28832
29625
  }
28833
29626
  async function uninstallLaunchd() {
28834
- const plistPath = path16.join(os11.homedir(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
28835
- if (!fs17.existsSync(plistPath)) {
29627
+ const plistPath = path17.join(os11.homedir(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
29628
+ if (!fs18.existsSync(plistPath)) {
28836
29629
  console.log(source_default.yellow(" \u26A0 Service is not installed"));
28837
29630
  return;
28838
29631
  }
@@ -28840,15 +29633,15 @@ async function uninstallLaunchd() {
28840
29633
  execSync3(`launchctl unload "${plistPath}" 2>/dev/null`, { encoding: "utf-8" });
28841
29634
  } catch {
28842
29635
  }
28843
- fs17.unlinkSync(plistPath);
29636
+ fs18.unlinkSync(plistPath);
28844
29637
  console.log(source_default.green(" \u2713 Agent service uninstalled (launchd)"));
28845
29638
  console.log(source_default.gray(" The agent will no longer start automatically."));
28846
29639
  }
28847
29640
  async function installSystemd(gateway, token) {
28848
29641
  const binPath = getAgentBinPath();
28849
- const serviceDir = path16.join(os11.homedir(), ".config", "systemd", "user");
28850
- const servicePath = path16.join(serviceDir, `${SYSTEMD_SERVICE}.service`);
28851
- fs17.mkdirSync(serviceDir, { recursive: true });
29642
+ const serviceDir = path17.join(os11.homedir(), ".config", "systemd", "user");
29643
+ const servicePath = path17.join(serviceDir, `${SYSTEMD_SERVICE}.service`);
29644
+ fs18.mkdirSync(serviceDir, { recursive: true });
28852
29645
  const unit = `[Unit]
28853
29646
  Description=nstantpage Local Development Agent
28854
29647
  After=network-online.target
@@ -28865,7 +29658,7 @@ Environment=HOME=${os11.homedir()}
28865
29658
  [Install]
28866
29659
  WantedBy=default.target
28867
29660
  `;
28868
- fs17.writeFileSync(servicePath, unit, "utf-8");
29661
+ fs18.writeFileSync(servicePath, unit, "utf-8");
28869
29662
  try {
28870
29663
  execSync3("systemctl --user daemon-reload", { encoding: "utf-8" });
28871
29664
  execSync3(`systemctl --user enable ${SYSTEMD_SERVICE}`, { encoding: "utf-8" });
@@ -28884,8 +29677,8 @@ WantedBy=default.target
28884
29677
  console.log(source_default.blue(' Open any project on nstantpage.com and click "Connect" to use it.\n'));
28885
29678
  }
28886
29679
  async function uninstallSystemd() {
28887
- const servicePath = path16.join(os11.homedir(), ".config", "systemd", "user", `${SYSTEMD_SERVICE}.service`);
28888
- if (!fs17.existsSync(servicePath)) {
29680
+ const servicePath = path17.join(os11.homedir(), ".config", "systemd", "user", `${SYSTEMD_SERVICE}.service`);
29681
+ if (!fs18.existsSync(servicePath)) {
28889
29682
  console.log(source_default.yellow(" \u26A0 Service is not installed"));
28890
29683
  return;
28891
29684
  }
@@ -28894,7 +29687,7 @@ async function uninstallSystemd() {
28894
29687
  execSync3(`systemctl --user disable ${SYSTEMD_SERVICE} 2>/dev/null`, { encoding: "utf-8" });
28895
29688
  } catch {
28896
29689
  }
28897
- fs17.unlinkSync(servicePath);
29690
+ fs18.unlinkSync(servicePath);
28898
29691
  try {
28899
29692
  execSync3("systemctl --user daemon-reload", { encoding: "utf-8" });
28900
29693
  } catch {
@@ -28905,14 +29698,14 @@ async function uninstallSystemd() {
28905
29698
  async function installWindowsTask(gateway, token) {
28906
29699
  const binPath = getAgentBinPath();
28907
29700
  const nodePath = getNodePath();
28908
- const logPath = path16.join(os11.homedir(), ".nstantpage", "agent.log");
28909
- fs17.mkdirSync(path16.dirname(logPath), { recursive: true });
28910
- const batchDir = path16.join(os11.homedir(), ".nstantpage");
28911
- const batchPath = path16.join(batchDir, "agent-service.cmd");
29701
+ const logPath = path17.join(os11.homedir(), ".nstantpage", "agent.log");
29702
+ fs18.mkdirSync(path17.dirname(logPath), { recursive: true });
29703
+ const batchDir = path17.join(os11.homedir(), ".nstantpage");
29704
+ const batchPath = path17.join(batchDir, "agent-service.cmd");
28912
29705
  const batchContent = `@echo off\r
28913
29706
  "${nodePath}" "${binPath}" start --gateway ${gateway} --token ${token} >> "${logPath}" 2>&1\r
28914
29707
  `;
28915
- fs17.writeFileSync(batchPath, batchContent, "utf-8");
29708
+ fs18.writeFileSync(batchPath, batchContent, "utf-8");
28916
29709
  try {
28917
29710
  execSync3(`schtasks /delete /tn "${WIN_TASK_NAME}" /f 2>nul`, { encoding: "utf-8" });
28918
29711
  } catch {
@@ -28942,21 +29735,148 @@ async function uninstallWindowsTask() {
28942
29735
  } catch {
28943
29736
  console.log(source_default.yellow(" \u26A0 Task is not installed or could not be removed"));
28944
29737
  }
28945
- const batchPath = path16.join(os11.homedir(), ".nstantpage", "agent-service.cmd");
29738
+ const batchPath = path17.join(os11.homedir(), ".nstantpage", "agent-service.cmd");
28946
29739
  try {
28947
- if (fs17.existsSync(batchPath))
28948
- fs17.unlinkSync(batchPath);
29740
+ if (fs18.existsSync(batchPath))
29741
+ fs18.unlinkSync(batchPath);
28949
29742
  } catch {
28950
29743
  }
28951
29744
  console.log(source_default.gray(" The agent will no longer start automatically."));
28952
29745
  }
28953
29746
 
29747
+ // dist/commands/update.js
29748
+ import fs19 from "fs";
29749
+ import path18 from "path";
29750
+ import os12 from "os";
29751
+ import { execSync as execSync4 } from "child_process";
29752
+ var DESKTOP_DIR = path18.join(os12.homedir(), ".nstantpage", "desktop");
29753
+ var VERSION_FILE = path18.join(DESKTOP_DIR, ".version");
29754
+ async function updateCommand(options = {}) {
29755
+ const updateBoth = !options.cli && !options.desktop;
29756
+ const currentVersion = getPackageVersion();
29757
+ console.log(source_default.blue(`
29758
+ nstantpage v${currentVersion}
29759
+ `));
29760
+ if (updateBoth || options.cli) {
29761
+ await updateCli(currentVersion);
29762
+ }
29763
+ if (updateBoth || options.desktop) {
29764
+ await updateDesktop();
29765
+ }
29766
+ console.log("");
29767
+ }
29768
+ async function updateCli(currentVersion) {
29769
+ console.log(source_default.gray(" Checking npm for CLI updates..."));
29770
+ try {
29771
+ const latest = execSync4("npm view nstantpage-agent version 2>/dev/null", {
29772
+ encoding: "utf-8"
29773
+ }).trim();
29774
+ if (latest === currentVersion) {
29775
+ console.log(source_default.green(` \u2713 CLI is up to date (${currentVersion})`));
29776
+ return;
29777
+ }
29778
+ console.log(source_default.yellow(` Updating CLI: ${currentVersion} \u2192 ${latest}`));
29779
+ execSync4("npm install -g nstantpage-agent@latest", {
29780
+ stdio: "inherit"
29781
+ });
29782
+ console.log(source_default.green(` \u2713 CLI updated to ${latest}`));
29783
+ } catch (err) {
29784
+ console.log(source_default.red(` \u2717 CLI update failed: ${err.message}`));
29785
+ console.log(source_default.gray(" Try manually: npm install -g nstantpage-agent@latest"));
29786
+ }
29787
+ }
29788
+ async function updateDesktop() {
29789
+ console.log(source_default.gray(" Checking GitHub for desktop updates..."));
29790
+ try {
29791
+ const scriptPath = path18.join(path18.dirname(new URL(import.meta.url).pathname), "..", "..", "scripts", "postinstall.mjs");
29792
+ if (fs19.existsSync(scriptPath)) {
29793
+ execSync4(`node "${scriptPath}"`, { stdio: "inherit" });
29794
+ } else {
29795
+ const altPath = path18.join(path18.dirname(new URL(import.meta.url).pathname), "..", "scripts", "postinstall.mjs");
29796
+ if (fs19.existsSync(altPath)) {
29797
+ execSync4(`node "${altPath}"`, { stdio: "inherit" });
29798
+ } else {
29799
+ console.log(source_default.yellow(" \u26A0 Desktop updater script not found. Reinstall to fix: npm i -g nstantpage-agent"));
29800
+ }
29801
+ }
29802
+ } catch (err) {
29803
+ console.log(source_default.red(` \u2717 Desktop update failed: ${err.message}`));
29804
+ }
29805
+ }
29806
+
29807
+ // dist/commands/run.js
29808
+ import fs20 from "fs";
29809
+ import path19 from "path";
29810
+ import os13 from "os";
29811
+ import { execSync as execSync5 } from "child_process";
29812
+ function findElectronApp2() {
29813
+ const platform2 = os13.platform();
29814
+ if (platform2 === "darwin") {
29815
+ const candidates = [
29816
+ path19.join(os13.homedir(), ".nstantpage", "desktop", "nstantpage.app"),
29817
+ "/Applications/nstantpage.app",
29818
+ path19.join(os13.homedir(), "Applications", "nstantpage.app")
29819
+ ];
29820
+ for (const p of candidates) {
29821
+ if (fs20.existsSync(p))
29822
+ return p;
29823
+ }
29824
+ } else if (platform2 === "win32") {
29825
+ const candidates = [
29826
+ path19.join(os13.homedir(), ".nstantpage", "desktop", "nstantpage.exe"),
29827
+ path19.join(os13.homedir(), "AppData", "Local", "Programs", "nstantpage", "nstantpage.exe"),
29828
+ path19.join("C:\\Program Files", "nstantpage", "nstantpage.exe")
29829
+ ];
29830
+ for (const p of candidates) {
29831
+ if (fs20.existsSync(p))
29832
+ return p;
29833
+ }
29834
+ }
29835
+ return null;
29836
+ }
29837
+ async function runCommand(options = {}) {
29838
+ const appPath = findElectronApp2();
29839
+ if (!appPath) {
29840
+ console.log(source_default.red(" \u2717 Desktop app not found."));
29841
+ console.log(source_default.gray(' Run "nstantpage update --desktop" to download it.'));
29842
+ process.exit(1);
29843
+ }
29844
+ const platform2 = os13.platform();
29845
+ const extraArgs = [];
29846
+ if (options.local !== void 0 && options.local !== false) {
29847
+ const port = typeof options.local === "string" ? options.local : "5001";
29848
+ extraArgs.push(`--local-url=http://localhost:${port}`);
29849
+ }
29850
+ console.log(source_default.blue(`
29851
+ Opening nstantpage desktop app...`));
29852
+ if (extraArgs.length) {
29853
+ console.log(source_default.gray(` Args: ${extraArgs.join(" ")}`));
29854
+ }
29855
+ try {
29856
+ if (platform2 === "darwin") {
29857
+ const argsStr = extraArgs.length ? ` --args ${extraArgs.join(" ")}` : "";
29858
+ execSync5(`open -a "${appPath}"${argsStr}`, { stdio: "inherit" });
29859
+ } else if (platform2 === "win32") {
29860
+ const argsStr = extraArgs.join(" ");
29861
+ execSync5(`"${appPath}" ${argsStr}`, { stdio: "inherit" });
29862
+ } else {
29863
+ console.log(source_default.yellow(" \u26A0 Desktop app not supported on this platform yet."));
29864
+ process.exit(1);
29865
+ }
29866
+ console.log(source_default.green(" \u2713 App launched\n"));
29867
+ } catch (err) {
29868
+ console.log(source_default.red(` \u2717 Failed to launch: ${err.message}`));
29869
+ process.exit(1);
29870
+ }
29871
+ }
29872
+
28954
29873
  // dist/cli.js
28955
29874
  var program2 = new Command();
28956
- program2.name("nstantpage").description("Local development agent for nstantpage.com \u2014 run projects on your machine, preview in the cloud").version("0.5.29");
29875
+ program2.name("nstantpage").description("Local development agent for nstantpage.com \u2014 run projects on your machine, preview in the cloud").version(getPackageVersion());
28957
29876
  program2.command("login").description("Authenticate with nstantpage.com").option("--gateway <url>", "Gateway URL (auto-detects local vs production)").option("--force", "Re-authenticate even if already logged in").action(loginCommand);
28958
29877
  program2.command("logout").description("Sign out and clear stored credentials").action(logoutCommand);
28959
29878
  program2.command("start").description("Start the agent for a project (replaces cloud containers)").argument("[directory]", "Project directory (defaults to ~/.nstantpage/projects/<id>)", ".").option("-p, --port <port>", "Local dev server port", "3000").option("-a, --api-port <port>", "Local API server port (internal)", "18924").option("--project-id <id>", "Link to a specific project ID").option("--gateway <url>", "Gateway URL (default: from login)").option("--backend <url>", "Backend API URL (auto-detected from gateway)").option("--token <token>", "Auth token (skip login flow)").option("--dir <path>", "Project directory override").option("--no-dev", "Skip starting the dev server (start manually later)").action(startCommand);
29879
+ program2.command("run").description("Open the nstantpage desktop app").option("--local [port]", "Connect to local backend (default: 5001)").action(runCommand);
28960
29880
  program2.command("stop").description("Stop the running agent").action(async () => {
28961
29881
  console.log(source_default.yellow("Stopping agent..."));
28962
29882
  const { getConfig: getConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
@@ -28982,4 +29902,5 @@ service.command("stop").description("Stop the running agent").action(serviceStop
28982
29902
  service.command("install").description("Install as a background service (auto-starts on boot)").option("--gateway <url>", "Gateway URL", "wss://webprev.live").action(serviceInstallCommand);
28983
29903
  service.command("uninstall").description("Remove the background service").action(serviceUninstallCommand);
28984
29904
  service.command("status").description("Check if the background service is running").action(serviceStatusCommand);
29905
+ program2.command("update").description("Update CLI and desktop app to the latest version").option("--cli", "Update only the CLI package").option("--desktop", "Update only the desktop app").action(updateCommand);
28985
29906
  program2.parse();