kly 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ import { n as formatPermissionsSummary, t as buildSandboxConfig } from "./kly.mjs";
2
+
3
+ export { buildSandboxConfig, formatPermissionsSummary };
package/dist/bin/kly.mjs CHANGED
@@ -950,9 +950,10 @@ function createResourceProvider(options) {
950
950
  * 4. Returns execution result
951
951
  */
952
952
  async function launchSandbox(options) {
953
- const { scriptPath, args: args$1, appId, sandboxConfig, allowApiKey } = options;
953
+ const { scriptPath, args: args$1, appId, invokeDir, sandboxConfig, allowApiKey } = options;
954
954
  await SandboxManager.initialize(sandboxConfig);
955
955
  const absoluteScriptPath = resolve(process.cwd(), scriptPath);
956
+ const _scriptDir = absoluteScriptPath.substring(0, absoluteScriptPath.lastIndexOf("/"));
956
957
  const executorPath = resolve(__dirname, "../sandbox/bundled-executor.mjs");
957
958
  if (!SandboxManager.isSandboxingEnabled()) {
958
959
  console.warn("⚠️ Sandboxing is not supported on this platform.");
@@ -973,6 +974,7 @@ async function launchSandbox(options) {
973
974
  "inherit",
974
975
  "ipc"
975
976
  ],
977
+ cwd: _scriptDir,
976
978
  env: {
977
979
  ...process.env,
978
980
  KLY_SANDBOX_MODE: "true"
@@ -994,6 +996,7 @@ async function launchSandbox(options) {
994
996
  scriptPath: absoluteScriptPath,
995
997
  args: args$1,
996
998
  appId,
999
+ invokeDir,
997
1000
  permissions: {
998
1001
  allowApiKey,
999
1002
  sandboxConfig
@@ -1843,22 +1846,42 @@ async function clearAllPermissionsAction() {
1843
1846
  */
1844
1847
  const PROTECTED_PATHS = {
1845
1848
  alwaysDenyWrite: [
1846
- join(homedir(), ".kly"),
1849
+ join(homedir(), ".kly/config"),
1850
+ join(homedir(), ".kly/permissions.json"),
1851
+ join(homedir(), ".kly/kly.sum"),
1847
1852
  join(homedir(), ".ssh"),
1848
1853
  join(homedir(), ".aws"),
1849
1854
  join(homedir(), ".gnupg")
1850
1855
  ],
1851
- alwaysDenyRead: [join(homedir(), ".kly")]
1856
+ alwaysDenyRead: [join(homedir(), ".kly/config"), join(homedir(), ".kly/permissions.json")]
1852
1857
  };
1853
1858
  /**
1859
+ * Resolve filesystem path with special marker support
1860
+ *
1861
+ * Special markers:
1862
+ * - "*": User's home directory (allows access to all non-sensitive files)
1863
+ * - Absolute paths: Used as-is
1864
+ *
1865
+ * @param path - Path to resolve (may contain special markers)
1866
+ * @returns Resolved absolute path, or undefined if path is undefined
1867
+ */
1868
+ function resolveFilesystemPath(path) {
1869
+ if (!path) return void 0;
1870
+ if (path === "*") return homedir();
1871
+ return path;
1872
+ }
1873
+ /**
1854
1874
  * Build a complete SandboxRuntimeConfig from declared app permissions
1855
1875
  *
1856
1876
  * This merges:
1857
1877
  * 1. Default safe configuration
1858
1878
  * 2. Automatic LLM domains (if apiKeys: true)
1859
- * 3. User-declared sandbox config
1879
+ * 3. User-declared sandbox config (with special marker support)
1860
1880
  * 4. Mandatory protections (always applied)
1861
1881
  *
1882
+ * Special markers in filesystem paths:
1883
+ * - "*": Allows access to all files in user's home directory (except sensitive paths)
1884
+ *
1862
1885
  * @param permissions - Declared app permissions
1863
1886
  * @returns Complete sandbox configuration ready for SandboxManager
1864
1887
  */
@@ -1870,10 +1893,16 @@ function buildSandboxConfig(permissions) {
1870
1893
  if (permissions?.apiKeys) allowedDomains = [...LLM_API_DOMAINS];
1871
1894
  if (permissions?.sandbox) {
1872
1895
  const userSandbox = permissions.sandbox;
1873
- if (userSandbox.network?.allowedDomains) allowedDomains = [...allowedDomains, ...userSandbox.network.allowedDomains];
1896
+ if (userSandbox.network?.allowedDomains) {
1897
+ const domains = userSandbox.network.allowedDomains.filter((d) => d !== void 0);
1898
+ allowedDomains = [...allowedDomains, ...domains];
1899
+ }
1874
1900
  if (userSandbox.filesystem) {
1875
- if (userSandbox.filesystem.allowWrite) allowWrite = userSandbox.filesystem.allowWrite;
1876
- if (userSandbox.filesystem.denyRead) denyRead = [...denyRead, ...userSandbox.filesystem.denyRead];
1901
+ if (userSandbox.filesystem.allowWrite) allowWrite = userSandbox.filesystem.allowWrite.map(resolveFilesystemPath).filter((p) => p !== void 0);
1902
+ if (userSandbox.filesystem.denyRead) {
1903
+ const deniedPaths = userSandbox.filesystem.denyRead.map(resolveFilesystemPath).filter((p) => p !== void 0);
1904
+ denyRead = [...denyRead, ...deniedPaths];
1905
+ }
1877
1906
  }
1878
1907
  }
1879
1908
  return {
@@ -1885,7 +1914,8 @@ function buildSandboxConfig(permissions) {
1885
1914
  denyRead,
1886
1915
  allowWrite,
1887
1916
  denyWrite: PROTECTED_PATHS.alwaysDenyWrite
1888
- }
1917
+ },
1918
+ allowPty: true
1889
1919
  };
1890
1920
  }
1891
1921
  /**
@@ -1904,9 +1934,13 @@ function formatPermissionsSummary(permissions) {
1904
1934
  summary.push(`• Network: ${domains}${more}`);
1905
1935
  }
1906
1936
  if (config.filesystem.allowWrite.length > 1 || config.filesystem.allowWrite.length === 1 && config.filesystem.allowWrite[0] !== currentDir) {
1907
- const dirs = config.filesystem.allowWrite.map((p) => p === currentDir ? "current directory" : p).slice(0, 2).join(", ");
1908
- const more = config.filesystem.allowWrite.length > 2 ? ` +${config.filesystem.allowWrite.length - 2} more` : "";
1909
- summary.push(`• Filesystem write: ${dirs}${more}`);
1937
+ const homeDir = homedir();
1938
+ if (config.filesystem.allowWrite.includes(homeDir)) summary.push("• Filesystem write: All non-sensitive directories");
1939
+ else {
1940
+ const dirs = config.filesystem.allowWrite.map((p) => p === currentDir ? "current directory" : p).slice(0, 2).join(", ");
1941
+ const more = config.filesystem.allowWrite.length > 2 ? ` +${config.filesystem.allowWrite.length - 2} more` : "";
1942
+ summary.push(`• Filesystem write: ${dirs}${more}`);
1943
+ }
1910
1944
  }
1911
1945
  if (permissions?.sandbox?.filesystem?.denyRead) summary.push(`• Filesystem read denied: ${permissions.sandbox.filesystem.denyRead.length} path(s)`);
1912
1946
  return summary;
@@ -2320,9 +2354,13 @@ const ENTRY_CANDIDATES = [
2320
2354
  ];
2321
2355
  /**
2322
2356
  * Resolve entry point for a kly app
2323
- * Priority: main field > convention candidates
2357
+ * Priority: convention candidates > main field
2358
+ *
2359
+ * We prioritize convention-based source files over package.json main field
2360
+ * because remote apps are run from source, not from built artifacts.
2324
2361
  */
2325
2362
  function resolveEntryPoint(repoPath) {
2363
+ for (const candidate of ENTRY_CANDIDATES) if (existsSync(join(repoPath, candidate))) return candidate;
2326
2364
  const pkgPath = join(repoPath, "package.json");
2327
2365
  if (existsSync(pkgPath)) try {
2328
2366
  const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
@@ -2330,7 +2368,6 @@ function resolveEntryPoint(repoPath) {
2330
2368
  if (existsSync(join(repoPath, pkg.main))) return pkg.main;
2331
2369
  }
2332
2370
  } catch {}
2333
- for (const candidate of ENTRY_CANDIDATES) if (existsSync(join(repoPath, candidate))) return candidate;
2334
2371
  return null;
2335
2372
  }
2336
2373
  /**
@@ -2703,20 +2740,40 @@ async function executeApp(ref, repoPath, args$1, mcp) {
2703
2740
  const prevRemoteRef = process.env[ENV_VARS.REMOTE_REF];
2704
2741
  process.env[ENV_VARS.REMOTE_REF] = remoteRef;
2705
2742
  try {
2706
- const { getAppIdentifier: getAppIdentifier$1, checkApiKeyPermission: checkApiKeyPermission$1, getAppSandboxConfig: getAppSandboxConfig$1 } = await import("./permissions-2r_7ZqaH.mjs");
2707
- const { launchSandbox: launchSandbox$1 } = await import("./launcher-vTpgdO9n.mjs");
2743
+ const { getAppIdentifier: getAppIdentifier$1, checkApiKeyPermission: checkApiKeyPermission$1, getAppSandboxConfig: getAppSandboxConfig$1 } = await import("./permissions-C_WgoA3t.mjs");
2744
+ const { launchSandbox: launchSandbox$1 } = await import("./launcher-Ex3ynZdE.mjs");
2745
+ const { buildSandboxConfig: buildSandboxConfig$1, formatPermissionsSummary: formatPermissionsSummary$1 } = await import("./config-builder-D5EtwOB3.mjs");
2746
+ const { extractAppPermissions: extractAppPermissions$1 } = await import("./permissions-extractor-BfUPS0Tr.mjs");
2708
2747
  const appId = getAppIdentifier$1();
2748
+ console.log("📋 Reading app permissions...");
2749
+ const declaredPermissions = await extractAppPermissions$1(absoluteEntryPath);
2750
+ if (declaredPermissions) {
2751
+ const summary = formatPermissionsSummary$1(declaredPermissions);
2752
+ if (summary.length > 0) {
2753
+ console.log("\nThis app requests the following permissions:");
2754
+ for (const item of summary) console.log(item);
2755
+ console.log("");
2756
+ }
2757
+ }
2709
2758
  console.log("🔐 Checking permissions...");
2710
- const allowApiKey = await checkApiKeyPermission$1(appId);
2711
- if (!allowApiKey) {
2712
- console.error("❌ Permission denied: API key access rejected");
2713
- process.exit(1);
2759
+ let allowApiKey = false;
2760
+ let sandboxConfig = null;
2761
+ if (declaredPermissions?.apiKeys) {
2762
+ allowApiKey = await checkApiKeyPermission$1(appId);
2763
+ if (!allowApiKey) {
2764
+ console.error("❌ Permission denied: API key access rejected");
2765
+ process.exit(1);
2766
+ }
2714
2767
  }
2715
- const sandboxConfig = await getAppSandboxConfig$1(appId);
2716
- if (!sandboxConfig) {
2717
- console.error("❌ Permission denied: Sandbox configuration rejected");
2718
- process.exit(1);
2768
+ if (declaredPermissions) sandboxConfig = buildSandboxConfig$1(declaredPermissions);
2769
+ else {
2770
+ sandboxConfig = await getAppSandboxConfig$1(appId);
2771
+ if (!sandboxConfig) {
2772
+ console.error("❌ Permission denied: Sandbox configuration rejected");
2773
+ process.exit(1);
2774
+ }
2719
2775
  }
2776
+ if (!sandboxConfig.filesystem.allowWrite.includes(repoPath)) sandboxConfig.filesystem.allowWrite.push(repoPath);
2720
2777
  if (mcp) {
2721
2778
  console.warn("⚠️ MCP mode with remote repos not yet fully supported in new architecture");
2722
2779
  process.env[ENV_VARS.MCP_MODE] = "true";
@@ -2728,6 +2785,7 @@ async function executeApp(ref, repoPath, args$1, mcp) {
2728
2785
  scriptPath: absoluteEntryPath,
2729
2786
  args: args$1,
2730
2787
  appId,
2788
+ invokeDir: process.cwd(),
2731
2789
  sandboxConfig,
2732
2790
  allowApiKey
2733
2791
  });
@@ -2799,6 +2857,7 @@ async function main() {
2799
2857
  }
2800
2858
  async function runFile(filePath, appArgs) {
2801
2859
  const absolutePath = resolve(process.cwd(), filePath);
2860
+ const invokeDir = process.cwd();
2802
2861
  const prevLocalRef = process.env.KLY_LOCAL_REF;
2803
2862
  process.env.KLY_LOCAL_REF = `local:${absolutePath}`;
2804
2863
  try {
@@ -2823,6 +2882,7 @@ async function runFile(filePath, appArgs) {
2823
2882
  scriptPath: absolutePath,
2824
2883
  args: appArgs,
2825
2884
  appId,
2885
+ invokeDir,
2826
2886
  sandboxConfig,
2827
2887
  allowApiKey
2828
2888
  });
@@ -2884,5 +2944,5 @@ main().catch((err) => {
2884
2944
  });
2885
2945
 
2886
2946
  //#endregion
2887
- export { getAppSandboxConfig as a, revokePermission as c, getAppName as i, savePermissions as l, clearAllPermissions as n, listPermissions as o, getAppIdentifier as r, loadPermissions as s, checkApiKeyPermission as t, launchSandbox as u };
2947
+ export { getAppIdentifier as a, listPermissions as c, savePermissions as d, launchSandbox as f, clearAllPermissions as i, loadPermissions as l, formatPermissionsSummary as n, getAppName as o, checkApiKeyPermission as r, getAppSandboxConfig as s, buildSandboxConfig as t, revokePermission as u };
2888
2948
  //# sourceMappingURL=kly.mjs.map