srcpack 0.1.1 → 0.1.2

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/README.md CHANGED
@@ -5,7 +5,7 @@ Zero-config CLI for bundling code into LLM-optimized context files.
5
5
  ## Quick Start
6
6
 
7
7
  ```bash
8
- npx srcpack --init # Create config interactively
8
+ npx srcpack init # Create config interactively
9
9
  npx srcpack # Bundle all
10
10
  ```
11
11
 
@@ -143,7 +143,7 @@ export function utils() {
143
143
  npx srcpack # Bundle all, upload if gdrive configured
144
144
  npx srcpack web api # Bundle specific bundles only
145
145
  npx srcpack --dry-run # Preview without writing files
146
- npx srcpack --init # Interactive config setup
146
+ npx srcpack init # Interactive config setup
147
147
  ```
148
148
 
149
149
  ## API
package/dist/cli.js CHANGED
@@ -2067,14 +2067,14 @@ var require_binary = __commonJS((exports, module) => {
2067
2067
  }
2068
2068
  return result;
2069
2069
  }
2070
- function isBinary(obj) {
2070
+ function isBinary2(obj) {
2071
2071
  return Object.prototype.toString.call(obj) === "[object Uint8Array]";
2072
2072
  }
2073
2073
  module.exports = new Type("tag:yaml.org,2002:binary", {
2074
2074
  kind: "scalar",
2075
2075
  resolve: resolveYamlBinary,
2076
2076
  construct: constructYamlBinary,
2077
- predicate: isBinary,
2077
+ predicate: isBinary2,
2078
2078
  represent: representYamlBinary
2079
2079
  });
2080
2080
  });
@@ -173852,6 +173852,15 @@ import { dirname as dirname3, join as join6 } from "path";
173852
173852
  var import_ignore = __toESM(require_ignore(), 1);
173853
173853
  import { join } from "path";
173854
173854
  var {Glob } = globalThis.Bun;
173855
+ var BINARY_CHECK_SIZE = 8192;
173856
+ async function isBinary(filePath) {
173857
+ const file = Bun.file(filePath);
173858
+ const size = file.size;
173859
+ if (size === 0)
173860
+ return false;
173861
+ const chunk = await file.slice(0, Math.min(size, BINARY_CHECK_SIZE)).bytes();
173862
+ return chunk.includes(0);
173863
+ }
173855
173864
  function normalizePatterns(config) {
173856
173865
  let patterns;
173857
173866
  if (typeof config === "string") {
@@ -173898,7 +173907,10 @@ async function resolvePatterns(config, cwd) {
173898
173907
  const glob = new Glob(pattern);
173899
173908
  for await (const match of glob.scan({ cwd, onlyFiles: true })) {
173900
173909
  if (!isExcluded(match, exclude) && !gitignore.ignores(match)) {
173901
- files.add(match);
173910
+ const fullPath = join(cwd, match);
173911
+ if (!await isBinary(fullPath)) {
173912
+ files.add(match);
173913
+ }
173902
173914
  }
173903
173915
  }
173904
173916
  }
@@ -187602,31 +187614,35 @@ import { homedir as homedir3 } from "os";
187602
187614
  import { dirname as dirname2, join as join4 } from "path";
187603
187615
 
187604
187616
  // node_modules/oauth-callback/dist/index.js
187605
- import process6 from "process";
187606
- import { Buffer as Buffer2 } from "buffer";
187617
+ import process7 from "process";
187607
187618
  import path from "path";
187608
187619
  import { fileURLToPath } from "url";
187609
- import { promisify as promisify5 } from "util";
187610
- import childProcess from "child_process";
187620
+ import childProcess3 from "child_process";
187611
187621
  import fs5, { constants as fsConstants2 } from "fs/promises";
187612
- import process22 from "process";
187622
+ import { promisify as promisify2 } from "util";
187623
+ import childProcess2 from "child_process";
187613
187624
  import fs4, { constants as fsConstants } from "fs/promises";
187614
187625
  import process3 from "process";
187615
187626
  import os from "os";
187616
187627
  import fs3 from "fs";
187617
187628
  import fs2 from "fs";
187618
187629
  import fs from "fs";
187619
- import { promisify as promisify4 } from "util";
187620
- import process5 from "process";
187621
- import { execFile as execFile4 } from "child_process";
187630
+ import process22 from "process";
187631
+ import { Buffer as Buffer2 } from "buffer";
187622
187632
  import { promisify } from "util";
187623
- import process32 from "process";
187624
- import { execFile } from "child_process";
187625
- import process4 from "process";
187626
- import { promisify as promisify2 } from "util";
187627
- import { execFile as execFile2, execFileSync } from "child_process";
187633
+ import childProcess from "child_process";
187634
+ import { promisify as promisify6 } from "util";
187635
+ import process5 from "process";
187636
+ import { execFile as execFile6 } from "child_process";
187628
187637
  import { promisify as promisify3 } from "util";
187638
+ import process32 from "process";
187629
187639
  import { execFile as execFile3 } from "child_process";
187640
+ import process4 from "process";
187641
+ import { promisify as promisify4 } from "util";
187642
+ import { execFile as execFile4, execFileSync } from "child_process";
187643
+ import { promisify as promisify5 } from "util";
187644
+ import { execFile as execFile5 } from "child_process";
187645
+ import process6 from "process";
187630
187646
  import * as fs6 from "fs/promises";
187631
187647
  import * as path2 from "path";
187632
187648
  import * as os2 from "os";
@@ -187695,6 +187711,45 @@ var isWsl = () => {
187695
187711
  }
187696
187712
  };
187697
187713
  var is_wsl_default = process3.env.__IS_WSL_TEST__ ? isWsl : isWsl();
187714
+ var execFile = promisify(childProcess.execFile);
187715
+ var powerShellPath = () => `${process22.env.SYSTEMROOT || process22.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
187716
+ var executePowerShell = async (command, options = {}) => {
187717
+ const {
187718
+ powerShellPath: psPath,
187719
+ ...execFileOptions
187720
+ } = options;
187721
+ const encodedCommand = executePowerShell.encodeCommand(command);
187722
+ return execFile(psPath ?? powerShellPath(), [
187723
+ ...executePowerShell.argumentsPrefix,
187724
+ encodedCommand
187725
+ ], {
187726
+ encoding: "utf8",
187727
+ ...execFileOptions
187728
+ });
187729
+ };
187730
+ executePowerShell.argumentsPrefix = [
187731
+ "-NoProfile",
187732
+ "-NonInteractive",
187733
+ "-ExecutionPolicy",
187734
+ "Bypass",
187735
+ "-EncodedCommand"
187736
+ ];
187737
+ executePowerShell.encodeCommand = (command) => Buffer2.from(command, "utf16le").toString("base64");
187738
+ executePowerShell.escapeArgument = (value) => `'${String(value).replaceAll("'", "''")}'`;
187739
+ function parseMountPointFromConfig(content) {
187740
+ for (const line of content.split(`
187741
+ `)) {
187742
+ if (/^\s*#/.test(line)) {
187743
+ continue;
187744
+ }
187745
+ const match = /^\s*root\s*=\s*(?<mountPoint>"[^"]*"|'[^']*'|[^#]*)/.exec(line);
187746
+ if (!match) {
187747
+ continue;
187748
+ }
187749
+ return match.groups.mountPoint.trim().replaceAll(/^["']|["']$/g, "");
187750
+ }
187751
+ }
187752
+ var execFile2 = promisify2(childProcess2.execFile);
187698
187753
  var wslDrivesMountPoint = (() => {
187699
187754
  const defaultMountPoint = "/mnt/";
187700
187755
  let mountPoint;
@@ -187712,11 +187767,11 @@ var wslDrivesMountPoint = (() => {
187712
187767
  return defaultMountPoint;
187713
187768
  }
187714
187769
  const configContent = await fs4.readFile(configFilePath, { encoding: "utf8" });
187715
- const configMountPoint = /(?<!#.*)root\s*=\s*(?<mountPoint>.*)/g.exec(configContent);
187716
- if (!configMountPoint) {
187770
+ const parsedMountPoint = parseMountPointFromConfig(configContent);
187771
+ if (parsedMountPoint === undefined) {
187717
187772
  return defaultMountPoint;
187718
187773
  }
187719
- mountPoint = configMountPoint.groups.mountPoint.trim();
187774
+ mountPoint = parsedMountPoint;
187720
187775
  mountPoint = mountPoint.endsWith("/") ? mountPoint : `${mountPoint}/`;
187721
187776
  return mountPoint;
187722
187777
  };
@@ -187725,11 +187780,36 @@ var powerShellPathFromWsl = async () => {
187725
187780
  const mountPoint = await wslDrivesMountPoint();
187726
187781
  return `${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`;
187727
187782
  };
187728
- var powerShellPath = async () => {
187729
- if (is_wsl_default) {
187730
- return powerShellPathFromWsl();
187783
+ var powerShellPath2 = is_wsl_default ? powerShellPathFromWsl : powerShellPath;
187784
+ var canAccessPowerShellPromise;
187785
+ var canAccessPowerShell = async () => {
187786
+ canAccessPowerShellPromise ??= (async () => {
187787
+ try {
187788
+ const psPath = await powerShellPath2();
187789
+ await fs4.access(psPath, fsConstants.X_OK);
187790
+ return true;
187791
+ } catch {
187792
+ return false;
187793
+ }
187794
+ })();
187795
+ return canAccessPowerShellPromise;
187796
+ };
187797
+ var wslDefaultBrowser = async () => {
187798
+ const psPath = await powerShellPath2();
187799
+ const command = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
187800
+ const { stdout } = await executePowerShell(command, { powerShellPath: psPath });
187801
+ return stdout.trim();
187802
+ };
187803
+ var convertWslPathToWindows = async (path3) => {
187804
+ if (/^[a-z]+:\/\//i.test(path3)) {
187805
+ return path3;
187806
+ }
187807
+ try {
187808
+ const { stdout } = await execFile2("wslpath", ["-aw", path3], { encoding: "utf8" });
187809
+ return stdout.trim();
187810
+ } catch {
187811
+ return path3;
187731
187812
  }
187732
- return `${process22.env.SYSTEMROOT || process22.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
187733
187813
  };
187734
187814
  function defineLazyProperty(object2, propertyName, valueGetter) {
187735
187815
  const define = (value) => Object.defineProperty(object2, propertyName, { value, enumerable: true, writable: true });
@@ -187747,7 +187827,7 @@ function defineLazyProperty(object2, propertyName, valueGetter) {
187747
187827
  });
187748
187828
  return object2;
187749
187829
  }
187750
- var execFileAsync = promisify(execFile);
187830
+ var execFileAsync = promisify3(execFile3);
187751
187831
  async function defaultBrowserId() {
187752
187832
  if (process32.platform !== "darwin") {
187753
187833
  throw new Error("macOS only");
@@ -187756,7 +187836,7 @@ async function defaultBrowserId() {
187756
187836
  const match = /LSHandlerRoleAll = "(?!-)(?<id>[^"]+?)";\s+?LSHandlerURLScheme = (?:http|https);/.exec(stdout);
187757
187837
  return match?.groups.id ?? "com.apple.Safari";
187758
187838
  }
187759
- var execFileAsync2 = promisify2(execFile2);
187839
+ var execFileAsync2 = promisify4(execFile4);
187760
187840
  async function runAppleScript(script, { humanReadableOutput = true } = {}) {
187761
187841
  if (process4.platform !== "darwin") {
187762
187842
  throw new Error("macOS only");
@@ -187769,18 +187849,26 @@ async function bundleName(bundleId) {
187769
187849
  return runAppleScript(`tell application "Finder" to set app_path to application file id "${bundleId}" as string
187770
187850
  tell application "System Events" to get value of property list item "CFBundleName" of property list file (app_path & ":Contents:Info.plist")`);
187771
187851
  }
187772
- var execFileAsync3 = promisify3(execFile3);
187852
+ var execFileAsync3 = promisify5(execFile5);
187773
187853
  var windowsBrowserProgIds = {
187774
- AppXq0fevzme2pys62n3e0fbqa7peapykr8v: { name: "Edge", id: "com.microsoft.edge.old" },
187775
- MSEdgeDHTML: { name: "Edge", id: "com.microsoft.edge" },
187776
187854
  MSEdgeHTM: { name: "Edge", id: "com.microsoft.edge" },
187777
- "IE.HTTP": { name: "Internet Explorer", id: "com.microsoft.ie" },
187778
- FirefoxURL: { name: "Firefox", id: "org.mozilla.firefox" },
187855
+ MSEdgeBHTML: { name: "Edge Beta", id: "com.microsoft.edge.beta" },
187856
+ MSEdgeDHTML: { name: "Edge Dev", id: "com.microsoft.edge.dev" },
187857
+ AppXq0fevzme2pys62n3e0fbqa7peapykr8v: { name: "Edge", id: "com.microsoft.edge.old" },
187779
187858
  ChromeHTML: { name: "Chrome", id: "com.google.chrome" },
187859
+ ChromeBHTML: { name: "Chrome Beta", id: "com.google.chrome.beta" },
187860
+ ChromeDHTML: { name: "Chrome Dev", id: "com.google.chrome.dev" },
187861
+ ChromiumHTM: { name: "Chromium", id: "org.chromium.Chromium" },
187780
187862
  BraveHTML: { name: "Brave", id: "com.brave.Browser" },
187781
187863
  BraveBHTML: { name: "Brave Beta", id: "com.brave.Browser.beta" },
187782
- BraveSSHTM: { name: "Brave Nightly", id: "com.brave.Browser.nightly" }
187864
+ BraveDHTML: { name: "Brave Dev", id: "com.brave.Browser.dev" },
187865
+ BraveSSHTM: { name: "Brave Nightly", id: "com.brave.Browser.nightly" },
187866
+ FirefoxURL: { name: "Firefox", id: "org.mozilla.firefox" },
187867
+ OperaStable: { name: "Opera", id: "com.operasoftware.Opera" },
187868
+ VivaldiHTM: { name: "Vivaldi", id: "com.vivaldi.Vivaldi" },
187869
+ "IE.HTTP": { name: "Internet Explorer", id: "com.microsoft.ie" }
187783
187870
  };
187871
+ var _windowsBrowserProgIdMap = new Map(Object.entries(windowsBrowserProgIds));
187784
187872
 
187785
187873
  class UnknownBrowserError extends Error {
187786
187874
  }
@@ -187802,7 +187890,7 @@ async function defaultBrowser(_execFileAsync = execFileAsync3) {
187802
187890
  }
187803
187891
  return browser;
187804
187892
  }
187805
- var execFileAsync4 = promisify4(execFile4);
187893
+ var execFileAsync4 = promisify6(execFile6);
187806
187894
  var titleize = (string4) => string4.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, (x) => x.toUpperCase());
187807
187895
  async function defaultBrowser2() {
187808
187896
  if (process5.platform === "darwin") {
@@ -187821,41 +187909,25 @@ async function defaultBrowser2() {
187821
187909
  }
187822
187910
  throw new Error("Only macOS, Linux, and Windows are supported");
187823
187911
  }
187824
- var execFile5 = promisify5(childProcess.execFile);
187825
- var __dirname2 = path.dirname(fileURLToPath(import.meta.url));
187912
+ var isInSsh = Boolean(process6.env.SSH_CONNECTION || process6.env.SSH_CLIENT || process6.env.SSH_TTY);
187913
+ var is_in_ssh_default = isInSsh;
187914
+ var fallbackAttemptSymbol = Symbol("fallbackAttempt");
187915
+ var __dirname2 = import.meta.url ? path.dirname(fileURLToPath(import.meta.url)) : "";
187826
187916
  var localXdgOpenPath = path.join(__dirname2, "xdg-open");
187827
- var { platform, arch } = process6;
187828
- async function getWindowsDefaultBrowserFromWsl() {
187829
- const powershellPath = await powerShellPath();
187830
- const rawCommand = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
187831
- const encodedCommand = Buffer2.from(rawCommand, "utf16le").toString("base64");
187832
- const { stdout } = await execFile5(powershellPath, [
187833
- "-NoProfile",
187834
- "-NonInteractive",
187835
- "-ExecutionPolicy",
187836
- "Bypass",
187837
- "-EncodedCommand",
187838
- encodedCommand
187839
- ], { encoding: "utf8" });
187840
- const progId = stdout.trim();
187841
- const browserMap = {
187842
- ChromeHTML: "com.google.chrome",
187843
- BraveHTML: "com.brave.Browser",
187844
- MSEdgeHTM: "com.microsoft.edge",
187845
- FirefoxURL: "org.mozilla.firefox"
187846
- };
187847
- return browserMap[progId] ? { id: browserMap[progId] } : {};
187848
- }
187849
- var pTryEach = async (array2, mapper) => {
187850
- let latestError;
187851
- for (const item of array2) {
187917
+ var { platform, arch } = process7;
187918
+ var tryEachApp = async (apps, opener) => {
187919
+ if (apps.length === 0) {
187920
+ return;
187921
+ }
187922
+ const errors3 = [];
187923
+ for (const app of apps) {
187852
187924
  try {
187853
- return await mapper(item);
187925
+ return await opener(app);
187854
187926
  } catch (error48) {
187855
- latestError = error48;
187927
+ errors3.push(error48);
187856
187928
  }
187857
187929
  }
187858
- throw latestError;
187930
+ throw new AggregateError(errors3, "Failed to open in all supported apps");
187859
187931
  };
187860
187932
  var baseOpen = async (options) => {
187861
187933
  options = {
@@ -187865,34 +187937,39 @@ var baseOpen = async (options) => {
187865
187937
  allowNonzeroExitCode: false,
187866
187938
  ...options
187867
187939
  };
187940
+ const isFallbackAttempt = options[fallbackAttemptSymbol] === true;
187941
+ delete options[fallbackAttemptSymbol];
187868
187942
  if (Array.isArray(options.app)) {
187869
- return pTryEach(options.app, (singleApp) => baseOpen({
187943
+ return tryEachApp(options.app, (singleApp) => baseOpen({
187870
187944
  ...options,
187871
- app: singleApp
187945
+ app: singleApp,
187946
+ [fallbackAttemptSymbol]: true
187872
187947
  }));
187873
187948
  }
187874
187949
  let { name: app, arguments: appArguments = [] } = options.app ?? {};
187875
187950
  appArguments = [...appArguments];
187876
187951
  if (Array.isArray(app)) {
187877
- return pTryEach(app, (appName) => baseOpen({
187952
+ return tryEachApp(app, (appName) => baseOpen({
187878
187953
  ...options,
187879
187954
  app: {
187880
187955
  name: appName,
187881
187956
  arguments: appArguments
187882
- }
187957
+ },
187958
+ [fallbackAttemptSymbol]: true
187883
187959
  }));
187884
187960
  }
187885
187961
  if (app === "browser" || app === "browserPrivate") {
187886
187962
  const ids = {
187887
187963
  "com.google.chrome": "chrome",
187888
187964
  "google-chrome.desktop": "chrome",
187889
- "com.brave.Browser": "brave",
187965
+ "com.brave.browser": "brave",
187890
187966
  "org.mozilla.firefox": "firefox",
187891
187967
  "firefox.desktop": "firefox",
187892
187968
  "com.microsoft.msedge": "edge",
187893
187969
  "com.microsoft.edge": "edge",
187894
187970
  "com.microsoft.edgemac": "edge",
187895
- "microsoft-edge.desktop": "edge"
187971
+ "microsoft-edge.desktop": "edge",
187972
+ "com.apple.safari": "safari"
187896
187973
  };
187897
187974
  const flags = {
187898
187975
  chrome: "--incognito",
@@ -187900,10 +187977,20 @@ var baseOpen = async (options) => {
187900
187977
  firefox: "--private-window",
187901
187978
  edge: "--inPrivate"
187902
187979
  };
187903
- const browser = is_wsl_default ? await getWindowsDefaultBrowserFromWsl() : await defaultBrowser2();
187980
+ let browser;
187981
+ if (is_wsl_default) {
187982
+ const progId = await wslDefaultBrowser();
187983
+ const browserInfo = _windowsBrowserProgIdMap.get(progId);
187984
+ browser = browserInfo ?? {};
187985
+ } else {
187986
+ browser = await defaultBrowser2();
187987
+ }
187904
187988
  if (browser.id in ids) {
187905
- const browserName = ids[browser.id];
187989
+ const browserName = ids[browser.id.toLowerCase()];
187906
187990
  if (app === "browserPrivate") {
187991
+ if (browserName === "safari") {
187992
+ throw new Error("Safari doesn't support opening in private mode via command line");
187993
+ }
187907
187994
  appArguments.push(flags[browserName]);
187908
187995
  }
187909
187996
  return baseOpen({
@@ -187919,6 +188006,10 @@ var baseOpen = async (options) => {
187919
188006
  let command;
187920
188007
  const cliArguments = [];
187921
188008
  const childProcessOptions = {};
188009
+ let shouldUseWindowsInWsl = false;
188010
+ if (is_wsl_default && !isInsideContainer() && !is_in_ssh_default && !app) {
188011
+ shouldUseWindowsInWsl = await canAccessPowerShell();
188012
+ }
187922
188013
  if (platform === "darwin") {
187923
188014
  command = "open";
187924
188015
  if (options.wait) {
@@ -187933,29 +188024,35 @@ var baseOpen = async (options) => {
187933
188024
  if (app) {
187934
188025
  cliArguments.push("-a", app);
187935
188026
  }
187936
- } else if (platform === "win32" || is_wsl_default && !isInsideContainer() && !app) {
187937
- command = await powerShellPath();
187938
- cliArguments.push("-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-EncodedCommand");
188027
+ } else if (platform === "win32" || shouldUseWindowsInWsl) {
188028
+ command = await powerShellPath2();
188029
+ cliArguments.push(...executePowerShell.argumentsPrefix);
187939
188030
  if (!is_wsl_default) {
187940
188031
  childProcessOptions.windowsVerbatimArguments = true;
187941
188032
  }
187942
- const encodedArguments = ["Start"];
188033
+ if (is_wsl_default && options.target) {
188034
+ options.target = await convertWslPathToWindows(options.target);
188035
+ }
188036
+ const encodedArguments = ["$ProgressPreference = 'SilentlyContinue';", "Start"];
187943
188037
  if (options.wait) {
187944
188038
  encodedArguments.push("-Wait");
187945
188039
  }
187946
188040
  if (app) {
187947
- encodedArguments.push(`"\`"${app}\`""`);
188041
+ encodedArguments.push(executePowerShell.escapeArgument(app));
187948
188042
  if (options.target) {
187949
188043
  appArguments.push(options.target);
187950
188044
  }
187951
188045
  } else if (options.target) {
187952
- encodedArguments.push(`"${options.target}"`);
188046
+ encodedArguments.push(executePowerShell.escapeArgument(options.target));
187953
188047
  }
187954
188048
  if (appArguments.length > 0) {
187955
- appArguments = appArguments.map((argument) => `"\`"${argument}\`""`);
188049
+ appArguments = appArguments.map((argument) => executePowerShell.escapeArgument(argument));
187956
188050
  encodedArguments.push("-ArgumentList", appArguments.join(","));
187957
188051
  }
187958
- options.target = Buffer2.from(encodedArguments.join(" "), "utf16le").toString("base64");
188052
+ options.target = executePowerShell.encodeCommand(encodedArguments.join(" "));
188053
+ if (!options.wait) {
188054
+ childProcessOptions.stdio = "ignore";
188055
+ }
187959
188056
  } else {
187960
188057
  if (app) {
187961
188058
  command = app;
@@ -187966,7 +188063,7 @@ var baseOpen = async (options) => {
187966
188063
  await fs5.access(localXdgOpenPath, fsConstants2.X_OK);
187967
188064
  exeLocalXdgOpen = true;
187968
188065
  } catch {}
187969
- const useSystemXdgOpen = process6.versions.electron ?? (platform === "android" || isBundled || !exeLocalXdgOpen);
188066
+ const useSystemXdgOpen = process7.versions.electron ?? (platform === "android" || isBundled || !exeLocalXdgOpen);
187970
188067
  command = useSystemXdgOpen ? "xdg-open" : localXdgOpenPath;
187971
188068
  }
187972
188069
  if (appArguments.length > 0) {
@@ -187983,12 +188080,12 @@ var baseOpen = async (options) => {
187983
188080
  if (options.target) {
187984
188081
  cliArguments.push(options.target);
187985
188082
  }
187986
- const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
188083
+ const subprocess = childProcess3.spawn(command, cliArguments, childProcessOptions);
187987
188084
  if (options.wait) {
187988
188085
  return new Promise((resolve, reject) => {
187989
188086
  subprocess.once("error", reject);
187990
188087
  subprocess.once("close", (exitCode) => {
187991
- if (!options.allowNonzeroExitCode && exitCode > 0) {
188088
+ if (!options.allowNonzeroExitCode && exitCode !== 0) {
187992
188089
  reject(new Error(`Exited with code ${exitCode}`));
187993
188090
  return;
187994
188091
  }
@@ -187996,8 +188093,30 @@ var baseOpen = async (options) => {
187996
188093
  });
187997
188094
  });
187998
188095
  }
188096
+ if (isFallbackAttempt) {
188097
+ return new Promise((resolve, reject) => {
188098
+ subprocess.once("error", reject);
188099
+ subprocess.once("spawn", () => {
188100
+ subprocess.once("close", (exitCode) => {
188101
+ subprocess.off("error", reject);
188102
+ if (exitCode !== 0) {
188103
+ reject(new Error(`Exited with code ${exitCode}`));
188104
+ return;
188105
+ }
188106
+ subprocess.unref();
188107
+ resolve(subprocess);
188108
+ });
188109
+ });
188110
+ });
188111
+ }
187999
188112
  subprocess.unref();
188000
- return subprocess;
188113
+ return new Promise((resolve, reject) => {
188114
+ subprocess.once("error", reject);
188115
+ subprocess.once("spawn", () => {
188116
+ subprocess.off("error", reject);
188117
+ resolve(subprocess);
188118
+ });
188119
+ });
188001
188120
  };
188002
188121
  var open = (target, options) => {
188003
188122
  if (typeof target !== "string") {
@@ -188018,7 +188137,7 @@ function detectArchBinary(binary) {
188018
188137
  }
188019
188138
  return archBinary;
188020
188139
  }
188021
- function detectPlatformBinary({ [platform]: platformBinary }, { wsl }) {
188140
+ function detectPlatformBinary({ [platform]: platformBinary }, { wsl } = {}) {
188022
188141
  if (wsl && is_wsl_default) {
188023
188142
  return detectArchBinary(wsl);
188024
188143
  }
@@ -188027,11 +188146,14 @@ function detectPlatformBinary({ [platform]: platformBinary }, { wsl }) {
188027
188146
  }
188028
188147
  return detectArchBinary(platformBinary);
188029
188148
  }
188030
- var apps = {};
188149
+ var apps = {
188150
+ browser: "browser",
188151
+ browserPrivate: "browserPrivate"
188152
+ };
188031
188153
  defineLazyProperty(apps, "chrome", () => detectPlatformBinary({
188032
188154
  darwin: "google chrome",
188033
188155
  win32: "chrome",
188034
- linux: ["google-chrome", "google-chrome-stable", "chromium"]
188156
+ linux: ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser"]
188035
188157
  }, {
188036
188158
  wsl: {
188037
188159
  ia32: "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",
@@ -188062,8 +188184,9 @@ defineLazyProperty(apps, "edge", () => detectPlatformBinary({
188062
188184
  }, {
188063
188185
  wsl: "/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"
188064
188186
  }));
188065
- defineLazyProperty(apps, "browser", () => "browser");
188066
- defineLazyProperty(apps, "browserPrivate", () => "browserPrivate");
188187
+ defineLazyProperty(apps, "safari", () => detectPlatformBinary({
188188
+ darwin: "Safari"
188189
+ }));
188067
188190
  var open_default = open;
188068
188191
 
188069
188192
  class OAuthError extends Error {
@@ -188155,6 +188278,7 @@ class BaseCallbackServer {
188155
188278
  successHtml;
188156
188279
  errorHtml;
188157
188280
  onRequest;
188281
+ callbackReceived = false;
188158
188282
  abortHandler;
188159
188283
  signal;
188160
188284
  setup(options) {
@@ -188180,6 +188304,7 @@ class BaseCallbackServer {
188180
188304
  for (const [key, value] of url2.searchParams)
188181
188305
  params[key] = value;
188182
188306
  listener.resolve(params);
188307
+ this.callbackReceived = true;
188183
188308
  return new Response(generateCallbackHTML(params, this.successHtml, this.errorHtml), {
188184
188309
  status: 200,
188185
188310
  headers: { "Content-Type": "text/html" }
@@ -188188,18 +188313,21 @@ class BaseCallbackServer {
188188
188313
  async waitForCallback(path22, timeout) {
188189
188314
  if (this.callbackListeners.has(path22))
188190
188315
  return Promise.reject(new Error(`A listener for the path "${path22}" is already active.`));
188316
+ let timeoutId;
188191
188317
  try {
188192
188318
  return await Promise.race([
188193
188319
  new Promise((resolve, reject) => {
188194
188320
  this.callbackListeners.set(path22, { resolve, reject });
188195
188321
  }),
188196
188322
  new Promise((_, reject) => {
188197
- setTimeout(() => {
188323
+ timeoutId = setTimeout(() => {
188198
188324
  reject(new Error(`OAuth callback timeout after ${timeout}ms waiting for ${path22}`));
188199
188325
  }, timeout);
188200
188326
  })
188201
188327
  ]);
188202
188328
  } finally {
188329
+ if (timeoutId)
188330
+ clearTimeout(timeoutId);
188203
188331
  this.callbackListeners.delete(path22);
188204
188332
  }
188205
188333
  }
@@ -188229,7 +188357,10 @@ class BunCallbackServer extends BaseCallbackServer {
188229
188357
  async stopServer() {
188230
188358
  if (!this.server)
188231
188359
  return;
188232
- this.server.stop();
188360
+ if (this.callbackReceived) {
188361
+ await new Promise((resolve) => setTimeout(resolve, 50));
188362
+ }
188363
+ this.server.stop(true);
188233
188364
  this.server = undefined;
188234
188365
  }
188235
188366
  }
@@ -188279,6 +188410,7 @@ class NodeCallbackServer extends BaseCallbackServer {
188279
188410
  async stopServer() {
188280
188411
  if (!this.server)
188281
188412
  return;
188413
+ this.server.closeAllConnections();
188282
188414
  return new Promise((resolve) => {
188283
188415
  this.server?.close(() => {
188284
188416
  this.server = undefined;
@@ -189356,10 +189488,12 @@ async function runInit() {
189356
189488
  }
189357
189489
  }
189358
189490
  const bundles = [];
189491
+ const existingNames = new Set;
189359
189492
  while (true) {
189360
- const bundle = await promptBundle(bundles.length === 0);
189493
+ const bundle = await promptBundle(bundles.length === 0, existingNames);
189361
189494
  if (!bundle)
189362
189495
  break;
189496
+ existingNames.add(bundle.name);
189363
189497
  bundles.push(bundle);
189364
189498
  const another = await ye({
189365
189499
  message: "Add another bundle?",
@@ -189387,15 +189521,18 @@ async function runInit() {
189387
189521
  await addToGitignore(cwd, outDirValue);
189388
189522
  Se(`Created ${CONFIG_FILE}`);
189389
189523
  }
189390
- async function promptBundle(isFirst) {
189524
+ async function promptBundle(isFirst, existingNames) {
189391
189525
  const name = await he({
189392
189526
  message: isFirst ? "Bundle name:" : "Next bundle name:",
189393
189527
  placeholder: "api",
189394
189528
  validate: (value) => {
189395
189529
  if (!value.trim())
189396
189530
  return "Name is required";
189397
- if (!/^[a-z][a-z0-9-]*$/i.test(value)) {
189398
- return "Use alphanumeric characters and hyphens";
189531
+ if (!/^[a-z][a-z0-9-]*$/.test(value)) {
189532
+ return "Use lowercase alphanumeric characters and hyphens";
189533
+ }
189534
+ if (existingNames.has(value.trim())) {
189535
+ return "Bundle name already exists";
189399
189536
  }
189400
189537
  }
189401
189538
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "srcpack",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Zero-config CLI for bundling code into LLM-optimized context files",
5
5
  "keywords": [
6
6
  "llm",
@@ -51,8 +51,12 @@
51
51
  "build": "bun build ./src/cli.ts ./src/index.ts --outdir ./dist --target bun && tsc",
52
52
  "typecheck": "tsc -p tsconfig.check.json",
53
53
  "check": "tsc -p tsconfig.check.json",
54
- "test": "bun test",
55
- "test:watch": "bun test --watch",
54
+ "test": "bun test tests/unit/ tests/e2e/",
55
+ "test:unit": "bun test tests/unit/",
56
+ "test:e2e": "bun test tests/e2e/",
57
+ "test:login": "bun test --env-file .env.local tests/manual/",
58
+ "test:all": "bun test --env-file .env.local tests/",
59
+ "test:watch": "bun test tests/unit/ tests/e2e/ --watch",
56
60
  "docs:dev": "vitepress dev",
57
61
  "docs:build": "vitepress build",
58
62
  "docs:preview": "vitepress preview",
@@ -62,7 +66,7 @@
62
66
  "@clack/prompts": "^0.11.0",
63
67
  "cosmiconfig": "^9.0.0",
64
68
  "ignore": "^7.0.5",
65
- "oauth-callback": "^1.2.1",
69
+ "oauth-callback": "^1.2.5",
66
70
  "zod": "^4.3.5"
67
71
  },
68
72
  "devDependencies": {
package/src/bundle.ts CHANGED
@@ -5,6 +5,18 @@ import { Glob } from "bun";
5
5
  import ignore, { type Ignore } from "ignore";
6
6
  import type { BundleConfigInput } from "./config.ts";
7
7
 
8
+ // Binary file detection: check first 8KB for null bytes (same heuristic as git)
9
+ const BINARY_CHECK_SIZE = 8192;
10
+
11
+ async function isBinary(filePath: string): Promise<boolean> {
12
+ const file = Bun.file(filePath);
13
+ const size = file.size;
14
+ if (size === 0) return false;
15
+
16
+ const chunk = await file.slice(0, Math.min(size, BINARY_CHECK_SIZE)).bytes();
17
+ return chunk.includes(0);
18
+ }
19
+
8
20
  export interface FileEntry {
9
21
  path: string; // Relative path from cwd
10
22
  lines: number; // Line count in source file
@@ -96,7 +108,10 @@ export async function resolvePatterns(
96
108
  const glob = new Glob(pattern);
97
109
  for await (const match of glob.scan({ cwd, onlyFiles: true })) {
98
110
  if (!isExcluded(match, exclude) && !gitignore.ignores(match)) {
99
- files.add(match);
111
+ const fullPath = join(cwd, match);
112
+ if (!(await isBinary(fullPath))) {
113
+ files.add(match);
114
+ }
100
115
  }
101
116
  }
102
117
  }
package/src/init.ts CHANGED
@@ -34,9 +34,11 @@ export async function runInit(): Promise<void> {
34
34
  const bundles: Bundle[] = [];
35
35
 
36
36
  // Collect bundles in a loop
37
+ const existingNames = new Set<string>();
37
38
  while (true) {
38
- const bundle = await promptBundle(bundles.length === 0);
39
+ const bundle = await promptBundle(bundles.length === 0, existingNames);
39
40
  if (!bundle) break;
41
+ existingNames.add(bundle.name);
40
42
  bundles.push(bundle);
41
43
 
42
44
  const another = await p.confirm({
@@ -76,14 +78,20 @@ export async function runInit(): Promise<void> {
76
78
  p.outro(`Created ${CONFIG_FILE}`);
77
79
  }
78
80
 
79
- async function promptBundle(isFirst: boolean): Promise<Bundle | null> {
81
+ async function promptBundle(
82
+ isFirst: boolean,
83
+ existingNames: Set<string>,
84
+ ): Promise<Bundle | null> {
80
85
  const name = await p.text({
81
86
  message: isFirst ? "Bundle name:" : "Next bundle name:",
82
87
  placeholder: "api",
83
88
  validate: (value) => {
84
89
  if (!value.trim()) return "Name is required";
85
- if (!/^[a-z][a-z0-9-]*$/i.test(value)) {
86
- return "Use alphanumeric characters and hyphens";
90
+ if (!/^[a-z][a-z0-9-]*$/.test(value)) {
91
+ return "Use lowercase alphanumeric characters and hyphens";
92
+ }
93
+ if (existingNames.has(value.trim())) {
94
+ return "Bundle name already exists";
87
95
  }
88
96
  },
89
97
  });
@@ -1,37 +0,0 @@
1
- import type { BundleConfigInput } from "./config.ts";
2
- export interface FileEntry {
3
- path: string;
4
- lines: number;
5
- startLine: number;
6
- endLine: number;
7
- }
8
- export interface BundleResult {
9
- content: string;
10
- index: FileEntry[];
11
- }
12
- /**
13
- * Resolve bundle config to a list of file paths.
14
- * Respects .gitignore patterns in the working directory.
15
- */
16
- export declare function resolvePatterns(config: BundleConfigInput, cwd: string): Promise<string[]>;
17
- /**
18
- * Format the index header block.
19
- * Format designed for LLM context files (ChatGPT, Grok, Gemini):
20
- * - Numbered entries for cross-reference with file separators
21
- * - ASCII-only characters for broad compatibility
22
- * - Line locations that point to actual file content
23
- */
24
- export declare function formatIndex(index: FileEntry[]): string;
25
- export interface BundleOptions {
26
- includeIndex?: boolean;
27
- }
28
- /**
29
- * Create a bundle from a list of files.
30
- * Line numbers in the index point to the first line of actual file content,
31
- * not to the separator line.
32
- */
33
- export declare function createBundle(files: string[], cwd: string, options?: BundleOptions): Promise<BundleResult>;
34
- /**
35
- * Bundle a single named bundle from config
36
- */
37
- export declare function bundleOne(name: string, config: BundleConfigInput, cwd: string): Promise<BundleResult>;
package/dist/src/cli.d.ts DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env bun
2
- export {};
@@ -1,45 +0,0 @@
1
- import { z } from "zod";
2
- export declare function expandPath(p: string): string;
3
- declare const BundleConfigSchema: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodObject<{
4
- include: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
5
- outfile: z.ZodOptional<z.ZodString>;
6
- index: z.ZodDefault<z.ZodBoolean>;
7
- }, z.core.$strip>]>;
8
- declare const UploadConfigSchema: z.ZodObject<{
9
- provider: z.ZodLiteral<"gdrive">;
10
- folder: z.ZodOptional<z.ZodString>;
11
- clientId: z.ZodString;
12
- clientSecret: z.ZodString;
13
- }, z.core.$strip>;
14
- declare const ConfigSchema: z.ZodObject<{
15
- outDir: z.ZodDefault<z.ZodString>;
16
- upload: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
17
- provider: z.ZodLiteral<"gdrive">;
18
- folder: z.ZodOptional<z.ZodString>;
19
- clientId: z.ZodString;
20
- clientSecret: z.ZodString;
21
- }, z.core.$strip>, z.ZodArray<z.ZodObject<{
22
- provider: z.ZodLiteral<"gdrive">;
23
- folder: z.ZodOptional<z.ZodString>;
24
- clientId: z.ZodString;
25
- clientSecret: z.ZodString;
26
- }, z.core.$strip>>]>>;
27
- bundles: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodObject<{
28
- include: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
29
- outfile: z.ZodOptional<z.ZodString>;
30
- index: z.ZodDefault<z.ZodBoolean>;
31
- }, z.core.$strip>]>>;
32
- }, z.core.$strip>;
33
- export type UploadConfig = z.infer<typeof UploadConfigSchema>;
34
- export type BundleConfig = z.infer<typeof BundleConfigSchema>;
35
- export type BundleConfigInput = z.input<typeof BundleConfigSchema>;
36
- export type Config = z.infer<typeof ConfigSchema>;
37
- export type ConfigInput = z.input<typeof ConfigSchema>;
38
- export declare function defineConfig(config: ConfigInput): ConfigInput;
39
- export declare class ConfigError extends Error {
40
- constructor(message: string);
41
- }
42
- export declare function parseConfig(value: unknown): Config;
43
- export declare function loadConfig(searchFrom?: string): Promise<Config | null>;
44
- export declare function loadConfigFromFile(filepath: string): Promise<Config | null>;
45
- export {};
@@ -1,33 +0,0 @@
1
- import { OAuthError } from "oauth-callback";
2
- import type { UploadConfig } from "./config.ts";
3
- export interface Tokens {
4
- access_token: string;
5
- refresh_token?: string;
6
- expires_at?: number;
7
- token_type: string;
8
- scope: string;
9
- }
10
- /**
11
- * Loads stored tokens from disk.
12
- * Returns null if no tokens exist or they cannot be read.
13
- */
14
- export declare function loadTokens(): Promise<Tokens | null>;
15
- /**
16
- * Removes stored tokens from disk.
17
- */
18
- export declare function clearTokens(): Promise<void>;
19
- /**
20
- * Gets valid tokens, refreshing if necessary.
21
- * Returns null if no tokens exist or refresh fails.
22
- */
23
- export declare function getValidTokens(config: UploadConfig): Promise<Tokens | null>;
24
- /**
25
- * Performs OAuth login flow - opens browser for user consent.
26
- * Stores tokens on success for future use.
27
- */
28
- export declare function login(config: UploadConfig): Promise<Tokens>;
29
- /**
30
- * Ensures we have valid tokens - loads existing or triggers login.
31
- */
32
- export declare function ensureAuthenticated(config: UploadConfig): Promise<Tokens>;
33
- export { OAuthError };
@@ -1,2 +0,0 @@
1
- export { defineConfig, loadConfig, loadConfigFromFile } from "./config.ts";
2
- export type { Config, BundleConfig } from "./config.ts";
@@ -1 +0,0 @@
1
- export declare function runInit(): Promise<void>;
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};