@runfusion/fusion 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Runfusion
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -20,13 +20,30 @@
20
20
 
21
21
  ## Install
22
22
 
23
- Zero install, straight from npm:
23
+ **Zero install, straight from npm:**
24
24
 
25
25
  ```bash
26
- npx @runfusion/fusion dashboard
26
+ npx runfusion.ai
27
27
  ```
28
28
 
29
- Globally:
29
+ Boots the dashboard. Subcommands forward through (`npx runfusion.ai task list`, etc). Long form: `npx @runfusion/fusion dashboard`.
30
+
31
+ **One-line installer** (macOS & Linux — auto-picks Homebrew, falls back to npm):
32
+
33
+ ```bash
34
+ curl -fsSL https://runfusion.ai/install.sh | sh
35
+ ```
36
+
37
+ **Homebrew** (macOS & Linux):
38
+
39
+ ```bash
40
+ brew tap runfusion/fusion
41
+ brew install fusion
42
+ ```
43
+
44
+ Or as a one-liner: `brew install runfusion/fusion/fusion`.
45
+
46
+ **npm global**:
30
47
 
31
48
  ```bash
32
49
  npm install -g @runfusion/fusion
package/dist/bin.js CHANGED
@@ -34457,7 +34457,7 @@ async function syncBackupAutomation(automationStore, settings) {
34457
34457
  if (!AutomationStore2.isValidCron(schedule)) {
34458
34458
  throw new Error(`Invalid backup schedule: ${schedule}`);
34459
34459
  }
34460
- const command = "fn backup --create";
34460
+ const command = "npx runfusion.ai backup --create";
34461
34461
  if (existingSchedule) {
34462
34462
  return await automationStore.updateSchedule(existingSchedule.id, {
34463
34463
  scheduleType: "custom",
@@ -34490,7 +34490,7 @@ async function syncBackupRoutine(routineStore, settings) {
34490
34490
  if (!RoutineStore2.isValidCron(schedule)) {
34491
34491
  throw new Error(`Invalid backup schedule: ${schedule}`);
34492
34492
  }
34493
- const command = "fn backup --create";
34493
+ const command = "npx runfusion.ai backup --create";
34494
34494
  const input = {
34495
34495
  name: BACKUP_SCHEDULE_NAME,
34496
34496
  description: "Automatic database backup based on project settings",
@@ -46017,10 +46017,10 @@ function pushAlias(aliases, value) {
46017
46017
  if (pathRef !== normalized && pathRef.length > 0) {
46018
46018
  aliases.add(pathRef);
46019
46019
  }
46020
- const basename13 = extractPathBasename(value);
46021
- if (basename13) {
46022
- aliases.add(basename13);
46023
- const basenameSlug = slugifyAgentReference(basename13);
46020
+ const basename14 = extractPathBasename(value);
46021
+ if (basename14) {
46022
+ aliases.add(basename14);
46023
+ const basenameSlug = slugifyAgentReference(basename14);
46024
46024
  if (basenameSlug.length > 0) {
46025
46025
  aliases.add(basenameSlug);
46026
46026
  }
@@ -50947,6 +50947,21 @@ var init_terminal_service = __esm({
50947
50947
  }
50948
50948
  return { shell: "/bin/sh", args: [] };
50949
50949
  }
50950
+ /**
50951
+ * Build concise diagnostics for PTY launch failures without logging the full environment.
50952
+ */
50953
+ getSpawnDiagnostics(requestedShell, detectedShell, detectedArgs, cwd) {
50954
+ return {
50955
+ platform: os2.platform(),
50956
+ projectRoot: this.projectRoot,
50957
+ cwd,
50958
+ requestedShell: requestedShell ?? null,
50959
+ detectedShell,
50960
+ detectedArgs,
50961
+ envShell: process.env.SHELL ?? null,
50962
+ allowedShells: this.getAllowedShells().filter((shellPath) => existsSync17(shellPath))
50963
+ };
50964
+ }
50950
50965
  /**
50951
50966
  * Validate and resolve a working directory path
50952
50967
  */
@@ -51073,6 +51088,7 @@ var init_terminal_service = __esm({
51073
51088
  };
51074
51089
  }
51075
51090
  const cwd = await this.resolveWorkingDirectory(options.cwd);
51091
+ const spawnDiagnostics = this.getSpawnDiagnostics(options.shell, detectedShell, shellArgs, cwd);
51076
51092
  const cleanEnv = {};
51077
51093
  for (const [key, value] of Object.entries(process.env)) {
51078
51094
  if (value !== void 0 && !STRIP_ENV_VARS.includes(key)) {
@@ -51088,7 +51104,11 @@ var init_terminal_service = __esm({
51088
51104
  LC_ALL: process.env.LC_ALL || process.env.LANG || "en_US.UTF-8",
51089
51105
  ...options.env
51090
51106
  };
51091
- console.info(`Creating session ${id} with shell: ${shell} in ${cwd}`);
51107
+ console.info(`[createSession] Creating session ${id}`, {
51108
+ ...spawnDiagnostics,
51109
+ selectedShell: shell,
51110
+ selectedArgs: shell === detectedShell ? shellArgs : []
51111
+ });
51092
51112
  let pty;
51093
51113
  try {
51094
51114
  pty = await loadPtyModule();
@@ -51110,11 +51130,44 @@ var init_terminal_service = __esm({
51110
51130
  if (this.isWindows) {
51111
51131
  ptyOptions.useConpty = false;
51112
51132
  }
51133
+ const attemptedSpawns = /* @__PURE__ */ new Set();
51134
+ const spawnAttempts = [];
51135
+ const addSpawnAttempt = (shellPath, args, reason) => {
51136
+ const key = `${shellPath}\0${args.join("\0")}`;
51137
+ if (attemptedSpawns.has(key)) return;
51138
+ attemptedSpawns.add(key);
51139
+ spawnAttempts.push({ shell: shellPath, args, reason });
51140
+ };
51141
+ addSpawnAttempt(shell, shellArgs, "primary");
51142
+ if (shellArgs.length > 0) {
51143
+ addSpawnAttempt(shell, [], "retry-without-login");
51144
+ }
51145
+ for (const allowedShell of this.getAllowedShells()) {
51146
+ if (allowedShell === shell || !existsSync17(allowedShell)) continue;
51147
+ const shellName = path.basename(allowedShell).toLowerCase().replace(".exe", "");
51148
+ const fallbackArgs = shellName === "bash" || shellName === "zsh" ? [] : [];
51149
+ addSpawnAttempt(allowedShell, fallbackArgs, "allowed-fallback");
51150
+ }
51113
51151
  let ptyProcess;
51114
- try {
51115
- ptyProcess = pty.spawn(shell, shellArgs, ptyOptions);
51116
- } catch (spawnError) {
51117
- console.error(`[createSession] PTY spawn failed:`, spawnError);
51152
+ let lastSpawnError;
51153
+ for (const attempt of spawnAttempts) {
51154
+ try {
51155
+ console.info(
51156
+ `[createSession] Spawning terminal via ${attempt.reason}: ${attempt.shell} ${attempt.args.join(" ")} in ${cwd}`
51157
+ );
51158
+ ptyProcess = pty.spawn(attempt.shell, attempt.args, ptyOptions);
51159
+ break;
51160
+ } catch (spawnError) {
51161
+ lastSpawnError = spawnError;
51162
+ console.error(
51163
+ `[createSession] PTY spawn failed (${attempt.reason}) for ${attempt.shell} ${attempt.args.join(" ")}:`,
51164
+ spawnError,
51165
+ spawnDiagnostics
51166
+ );
51167
+ }
51168
+ }
51169
+ if (!ptyProcess) {
51170
+ console.error(`[createSession] All PTY spawn attempts failed`, lastSpawnError, spawnDiagnostics);
51118
51171
  return {
51119
51172
  success: false,
51120
51173
  code: "pty_spawn_failed",
@@ -51393,7 +51446,7 @@ var init_terminal_service = __esm({
51393
51446
  });
51394
51447
 
51395
51448
  // ../dashboard/src/file-service.ts
51396
- import { join as join23, resolve as resolve12, relative as relative3, dirname as dirname8, basename as basename6 } from "node:path";
51449
+ import { join as join23, resolve as resolve12, relative as relative3, dirname as dirname8, basename as basename7 } from "node:path";
51397
51450
  import { readdir as readdir7, readFile as fsReadFile, writeFile as fsWriteFile, stat as stat5, copyFile as fsCopyFile, rename as fsRename, rm as fsRm, mkdir as mkdir11, access as access3 } from "node:fs/promises";
51398
51451
  async function getTaskBasePath(store, taskId) {
51399
51452
  try {
@@ -51847,7 +51900,7 @@ async function getWorkspaceFileForDownload(store, workspace, filePath) {
51847
51900
  mtime: stats.mtime,
51848
51901
  isFile: true
51849
51902
  },
51850
- fileName: basename6(resolvedPath)
51903
+ fileName: basename7(resolvedPath)
51851
51904
  };
51852
51905
  }
51853
51906
  async function getWorkspaceFolderForZip(store, workspace, dirPath) {
@@ -51875,7 +51928,7 @@ async function getWorkspaceFolderForZip(store, workspace, dirPath) {
51875
51928
  }
51876
51929
  return {
51877
51930
  absolutePath: resolvedPath,
51878
- dirName: basename6(resolvedPath)
51931
+ dirName: basename7(resolvedPath)
51879
51932
  };
51880
51933
  }
51881
51934
  async function walkDirForMarkdown(basePath, currentRelative, results) {
@@ -60432,7 +60485,7 @@ __export(pi_exports, {
60432
60485
  import { existsSync as existsSync21, readFileSync as readFileSync8 } from "node:fs";
60433
60486
  import { exec } from "node:child_process";
60434
60487
  import { promisify as promisify2 } from "node:util";
60435
- import { basename as basename7, dirname as dirname9, join as join28, relative as relative4, isAbsolute as isAbsolute6, resolve as resolve13 } from "node:path";
60488
+ import { basename as basename8, dirname as dirname9, join as join28, relative as relative4, isAbsolute as isAbsolute6, resolve as resolve13 } from "node:path";
60436
60489
  import { createAgentSession, createCodingTools, createExtensionRuntime, createReadOnlyTools, DefaultResourceLoader, DefaultPackageManager, discoverAndLoadExtensions, ModelRegistry, SessionManager, SettingsManager } from "@mariozechner/pi-coding-agent";
60437
60490
  function getSessionStateError(session) {
60438
60491
  const error = session.state?.error;
@@ -60645,7 +60698,7 @@ function hasPackageManagerSettings(settings) {
60645
60698
  return Array.isArray(settings.packages) || Array.isArray(settings.npmCommand);
60646
60699
  }
60647
60700
  function siblingAgentDir(agentDir, siblingRoot) {
60648
- if (basename7(agentDir) !== "agent") {
60701
+ if (basename8(agentDir) !== "agent") {
60649
60702
  return void 0;
60650
60703
  }
60651
60704
  return join28(dirname9(dirname9(agentDir)), siblingRoot, "agent");
@@ -87057,7 +87110,7 @@ var init_rate_limit = __esm({
87057
87110
  // ../dashboard/src/plugin-routes.ts
87058
87111
  import { Router as Router5 } from "express";
87059
87112
  import { access as access5, stat as stat8, readFile as readFile21 } from "node:fs/promises";
87060
- import { join as join41, isAbsolute as isAbsolute11, dirname as dirname13, basename as basename8 } from "node:path";
87113
+ import { join as join41, isAbsolute as isAbsolute11, dirname as dirname13, basename as basename9 } from "node:path";
87061
87114
  async function resolvePluginManifest(sourcePath) {
87062
87115
  try {
87063
87116
  await access5(sourcePath);
@@ -87081,7 +87134,7 @@ async function resolvePluginManifest(sourcePath) {
87081
87134
  } catch (err) {
87082
87135
  if (err instanceof ApiError) throw err;
87083
87136
  }
87084
- const dirName = basename8(sourcePath).toLowerCase();
87137
+ const dirName = basename9(sourcePath).toLowerCase();
87085
87138
  if (DIST_DIR_NAMES.has(dirName)) {
87086
87139
  const parentDir = dirname13(sourcePath);
87087
87140
  const parentManifestPath = join41(parentDir, "manifest.json");
@@ -124010,7 +124063,7 @@ ${body}`;
124010
124063
  const nodeId = req.query.nodeId;
124011
124064
  if (nodeId) {
124012
124065
  const { CentralCore: CentralCore2 } = await Promise.resolve().then(() => (init_src(), src_exports));
124013
- const central = new CentralCore2();
124066
+ const central = new CentralCore2(store.getFusionDir());
124014
124067
  await central.init();
124015
124068
  const localNodes = await central.listNodes();
124016
124069
  const localNode = localNodes.find((n) => n.type === "local");
@@ -124037,6 +124090,9 @@ ${body}`;
124037
124090
  headers,
124038
124091
  signal: AbortSignal.timeout(3e4)
124039
124092
  });
124093
+ if (proxyRes.status === 401 && !node.apiKey) {
124094
+ throw unauthorized("Remote node rejected directory browse request. Configure an apiKey for that node in Fusion settings.");
124095
+ }
124040
124096
  const body = Buffer.from(await proxyRes.arrayBuffer());
124041
124097
  const skipHeaders = /* @__PURE__ */ new Set(["connection", "keep-alive", "transfer-encoding", "upgrade"]);
124042
124098
  for (const [key, value] of proxyRes.headers.entries()) {
@@ -133089,9 +133145,9 @@ var init_port_prompt = __esm({
133089
133145
 
133090
133146
  // src/commands/provider-settings.ts
133091
133147
  import { existsSync as existsSync31, readFileSync as readFileSync11, writeFileSync as writeFileSync2, mkdirSync as mkdirSync6 } from "node:fs";
133092
- import { join as join46, dirname as dirname16, basename as basename9 } from "node:path";
133148
+ import { join as join46, dirname as dirname16, basename as basename10 } from "node:path";
133093
133149
  function siblingAgentDir2(agentDir, siblingRoot) {
133094
- if (basename9(agentDir) !== "agent") {
133150
+ if (basename10(agentDir) !== "agent") {
133095
133151
  return void 0;
133096
133152
  }
133097
133153
  return join46(dirname16(dirname16(agentDir)), siblingRoot, "agent");
@@ -135731,10 +135787,10 @@ async function runTaskCreate(descriptionArg, attachFiles, depends, projectName)
135731
135787
  console.log(` Path: .fusion/tasks/${task.id}/`);
135732
135788
  if (attachFiles && attachFiles.length > 0) {
135733
135789
  const { readFile: readFile24 } = await import("node:fs/promises");
135734
- const { basename: basename13, extname, resolve: resolve29 } = await import("node:path");
135790
+ const { basename: basename14, extname, resolve: resolve29 } = await import("node:path");
135735
135791
  for (const filePath of attachFiles) {
135736
135792
  const resolvedPath = resolve29(filePath);
135737
- const filename = basename13(resolvedPath);
135793
+ const filename = basename14(resolvedPath);
135738
135794
  const ext = extname(filename).toLowerCase();
135739
135795
  const mimeType = MIME_TYPES[ext];
135740
135796
  if (!mimeType) {
@@ -135980,10 +136036,10 @@ async function runTaskMerge(id, projectName) {
135980
136036
  }
135981
136037
  async function runTaskAttach(id, filePath, projectName) {
135982
136038
  const { readFile: readFile24 } = await import("node:fs/promises");
135983
- const { basename: basename13, extname } = await import("node:path");
136039
+ const { basename: basename14, extname } = await import("node:path");
135984
136040
  const { resolve: resolve29 } = await import("node:path");
135985
136041
  const resolvedPath = resolve29(filePath);
135986
- const filename = basename13(resolvedPath);
136042
+ const filename = basename14(resolvedPath);
135987
136043
  const ext = extname(filename).toLowerCase();
135988
136044
  const mimeType = MIME_TYPES[ext];
135989
136045
  if (!mimeType) {
@@ -138204,7 +138260,7 @@ __export(project_exports, {
138204
138260
  runProjectSetDefault: () => runProjectSetDefault,
138205
138261
  runProjectShow: () => runProjectShow
138206
138262
  });
138207
- import { resolve as resolve25, isAbsolute as isAbsolute13, relative as relative11, basename as basename10 } from "node:path";
138263
+ import { resolve as resolve25, isAbsolute as isAbsolute13, relative as relative11, basename as basename11 } from "node:path";
138208
138264
  import { existsSync as existsSync38, statSync as statSync9 } from "node:fs";
138209
138265
  import { createInterface as createInterface7 } from "node:readline/promises";
138210
138266
  function formatDisplayPath(projectPath) {
@@ -138212,7 +138268,7 @@ function formatDisplayPath(projectPath) {
138212
138268
  if (rel && !rel.startsWith("..") && rel !== "") {
138213
138269
  return rel;
138214
138270
  }
138215
- return basename10(projectPath) || ".";
138271
+ return basename11(projectPath) || ".";
138216
138272
  }
138217
138273
  function formatLastActivity2(timestamp) {
138218
138274
  if (!timestamp) return "never";
@@ -138362,7 +138418,7 @@ async function runProjectAdd(name, path4, options = {}) {
138362
138418
  }
138363
138419
  }
138364
138420
  if (!projectName) {
138365
- const suggested = basename10(absolutePath2);
138421
+ const suggested = basename11(absolutePath2);
138366
138422
  projectName = await rl.question(` Project name [${suggested}]: `);
138367
138423
  projectName = projectName.trim() || suggested;
138368
138424
  }
@@ -138651,7 +138707,7 @@ __export(init_exports, {
138651
138707
  runInit: () => runInit
138652
138708
  });
138653
138709
  import { existsSync as existsSync39, mkdirSync as mkdirSync7, writeFileSync as writeFileSync3, readFileSync as readFileSync15 } from "node:fs";
138654
- import { join as join54, resolve as resolve26, basename as basename11 } from "node:path";
138710
+ import { join as join54, resolve as resolve26, basename as basename12 } from "node:path";
138655
138711
  import { exec as exec11 } from "node:child_process";
138656
138712
  import { promisify as promisify13 } from "node:util";
138657
138713
  async function runInit(options = {}) {
@@ -138767,7 +138823,7 @@ async function detectProjectName(dir2) {
138767
138823
  }
138768
138824
  } catch {
138769
138825
  }
138770
- return basename11(dir2) || "my-project";
138826
+ return basename12(dir2) || "my-project";
138771
138827
  }
138772
138828
  async function addLocalStorageToGitignore(cwd) {
138773
138829
  const gitignorePath = join54(cwd, ".gitignore");
@@ -139949,7 +140005,7 @@ __export(native_patch_exports, {
139949
140005
  isTerminalAvailable: () => isTerminalAvailable,
139950
140006
  setupNativeResolution: () => setupNativeResolution
139951
140007
  });
139952
- import { join as join57, basename as basename12, dirname as dirname19 } from "node:path";
140008
+ import { join as join57, basename as basename13, dirname as dirname19 } from "node:path";
139953
140009
  import { existsSync as existsSync43, copyFileSync, mkdirSync as mkdirSync10, symlinkSync, rmSync as rmSync3, lstatSync as lstatSync2, readlinkSync } from "node:fs";
139954
140010
  import { tmpdir as tmpdir3 } from "node:os";
139955
140011
  function findStagedNativeDir2() {
@@ -139999,7 +140055,7 @@ function setupNativeResolution() {
139999
140055
  const tmpRoot = join57(tmpdir3(), `fn-bunfs-${process.pid}`);
140000
140056
  const fnDir = join57(tmpRoot, "fn");
140001
140057
  const prebuildsDir = join57(fnDir, "prebuilds");
140002
- const platformDir = join57(prebuildsDir, basename12(nativeDir));
140058
+ const platformDir = join57(prebuildsDir, basename13(nativeDir));
140003
140059
  try {
140004
140060
  cleanupStaleBunfsLinks();
140005
140061
  mkdirSync10(platformDir, { recursive: true });