@vimoxshah/tokenflow 1.1.1 → 1.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/CHANGELOG.md CHANGED
@@ -3,6 +3,54 @@
3
3
  All notable changes to TokenFlow are recorded here. Versions follow
4
4
  [semantic versioning](https://semver.org/spec/v2.0.0.html).
5
5
 
6
+ ## 1.1.2 — 2026-09-03
7
+
8
+ Makes the downloadable app usable on a machine that is not the one that built
9
+ it. 1.1.1 was correct as source and as an npm package; its DMG was not.
10
+
11
+ ### Fixed
12
+
13
+ - **The released app pointed at the machine that built it.** Every build
14
+ embedded the absolute path of its own node binary and CLI, which is right for
15
+ a developer driving their checkout and wrong for a release: the 1.1.1 DMG
16
+ shipped `TokenFlowCLIPath = /Users/runner/work/tokenflow/...`, a path that
17
+ exists on no user's machine. Only path-discovery fallbacks kept the app
18
+ working at all. A distributable build now embeds no path
19
+ (`TOKENFLOW_PORTABLE=1`), the DMG script refuses to package one that does,
20
+ and the release workflow re-checks it after mounting the finished image.
21
+ - **A cask-only install had nothing to run.** The cask installs the app; the
22
+ CLI came from npm. Anyone who only ran `brew install --cask tokenflow` got a
23
+ menu bar that could read an existing status file and do nothing else — no
24
+ refresh, no watcher, no dashboard. The app now carries its own CLI (see
25
+ below).
26
+ - **Node discovery named one specific version.** The first candidate was
27
+ `~/.nvm/versions/node/v24.13.1/bin/node` — whichever version the developer
28
+ happened to have. One `nvm install` away from being wrong for everybody.
29
+ nvm installs are now discovered and the highest one at or above the engine
30
+ floor wins.
31
+
32
+ ### Added
33
+
34
+ - **The app bundles its own CLI**, at
35
+ `Contents/Resources/cli/package/bin/tokenflow.js`. It is packed with
36
+ `npm pack` and then pruned to the files the CLI actually executes, so the
37
+ bundle is always a subset of the published package and never carries a file
38
+ npm does not ship. The bundled copy takes priority over any CLI found on the
39
+ system, because the app and the CLI share a contract — the status file's
40
+ shape, the watcher lock format, `/api/ping` — and the copy shipped beside the
41
+ binary is the only one guaranteed to match it. A local build still embeds the
42
+ developer's clone, which wins, so an installed app keeps driving the checkout
43
+ being edited.
44
+ - **A missing dependency now says so.** With no CLI or no Node the menu bar
45
+ reported nothing and every button failed silently. It names the problem and
46
+ the command that fixes it, at launch rather than on the first click.
47
+
48
+ ### Changed
49
+
50
+ - The cask states the Node requirement in `caveats` instead of declaring
51
+ `depends_on formula: "node"`, which would install a second Node beside an
52
+ nvm- or asdf-managed one and fight the version manager.
53
+
6
54
  ## 1.1.1 — 2026-09-03
7
55
 
8
56
  A reliability release. Five defects had combined to leave the app paused, the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vimoxshah/tokenflow",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Local-first, provider-agnostic analytics for the AI tokens you actually spend. Zero dependencies, nothing leaves your machine.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -15,8 +15,17 @@ DIST="$REPO/dist"
15
15
  APP="$DIST/TokenFlow.app"
16
16
  DMG="$DIST/TokenFlow-$VERSION.dmg"
17
17
 
18
- "$REPO/scripts/build-menubar-app.sh" "$DIST" "$VERSION" >/dev/null
19
- echo "built TokenFlow.app"
18
+ # A DMG is for other people's machines, so it must carry no path from this one.
19
+ TOKENFLOW_PORTABLE=1 "$REPO/scripts/build-menubar-app.sh" "$DIST" "$VERSION" >/dev/null
20
+ echo "built TokenFlow.app (portable)"
21
+
22
+ # Fail here rather than shipping an app that points at the build host.
23
+ for KEY in TokenFlowCLIPath TokenFlowNodePath; do
24
+ if /usr/libexec/PlistBuddy -c "Print :$KEY" "$APP/Contents/Info.plist" >/dev/null 2>&1; then
25
+ echo "error: $KEY is embedded in a distributable build" >&2
26
+ exit 1
27
+ fi
28
+ done
20
29
 
21
30
  STAGING="$(mktemp -d)"
22
31
  trap 'rm -rf "$STAGING"' EXIT
@@ -4,8 +4,24 @@
4
4
  # scripts/build-menubar-app.sh [output-dir]
5
5
  #
6
6
  # Compiles menubar/TokenFlow/main.swift with swiftc (Xcode Command Line Tools)
7
- # into a minimal .app bundle, embedding the absolute paths of this clone's
8
- # node binary and CLI so the app can drive refresh/watch actions.
7
+ # into a minimal .app bundle.
8
+ #
9
+ # The CLI is ALWAYS bundled into Contents/Resources/cli, packed with `npm pack`
10
+ # so the copy inside the app is byte-for-byte the published package rather than
11
+ # a hand-picked subset of the working tree. The app drives that copy, which is
12
+ # the only one guaranteed to match the binary it ships beside: the app and the
13
+ # CLI share a contract (the status file, the watcher lock format, /api/ping),
14
+ # and an unrelated CLI version next door is a mismatch nobody can reason about.
15
+ #
16
+ # Two build flavours:
17
+ #
18
+ # local (default) also embeds this clone's absolute node + CLI paths, so
19
+ # a developer's installed app drives the checkout they
20
+ # are editing.
21
+ # TOKENFLOW_PORTABLE=1 embeds NO absolute paths. Anything built for
22
+ # distribution must use this: a release built on CI
23
+ # otherwise ships /Users/runner/... in its Info.plist,
24
+ # which exists on no user's machine.
9
25
  set -euo pipefail
10
26
 
11
27
  REPO="$(cd "$(dirname "$0")/.." && pwd)"
@@ -25,14 +41,40 @@ command -v swiftc >/dev/null 2>&1 || {
25
41
  NODE_BIN="$(command -v node)"
26
42
  CLI_JS="$REPO/bin/tokenflow.js"
27
43
  [ -f "$CLI_JS" ] || { echo "error: $CLI_JS missing" >&2; exit 1; }
44
+ PORTABLE="${TOKENFLOW_PORTABLE:-0}"
28
45
 
29
46
  TMP="$(mktemp -d)"
30
47
  trap 'rm -rf "$TMP"' EXIT
31
48
 
49
+ rm -rf "$APP"
32
50
  mkdir -p "$APP/Contents/MacOS"
33
51
  mkdir -p "$APP/Contents/Resources"
34
52
  cp "$REPO/menubar/TokenFlow/AppIcon.icns" "$APP/Contents/Resources/AppIcon.icns"
35
53
 
54
+ # ---- bundle the CLI ---------------------------------------------------------
55
+ # `npm pack` rather than copying bin/ and src/: the tarball is what npm
56
+ # publishes, filtered by package.json "files", so the app can never ship a file
57
+ # the package does not.
58
+ echo "packing the CLI into the bundle"
59
+ ( cd "$REPO" && npm pack --silent --pack-destination "$TMP" >/dev/null )
60
+ TGZ="$(ls "$TMP"/*.tgz | head -1)"
61
+ [ -f "$TGZ" ] || { echo "error: npm pack produced no tarball" >&2; exit 1; }
62
+ mkdir -p "$APP/Contents/Resources/cli"
63
+ tar -xzf "$TGZ" -C "$APP/Contents/Resources/cli"
64
+ BUNDLED_CLI="$APP/Contents/Resources/cli/package/bin/tokenflow.js"
65
+ [ -f "$BUNDLED_CLI" ] || { echo "error: bundled CLI missing at $BUNDLED_CLI" >&2; exit 1; }
66
+
67
+ # Keep only what the CLI actually executes. docs/, skills/, examples/ and
68
+ # scripts/ are never read at runtime — they appear in printed hints and nothing
69
+ # opens them — and they are four fifths of the tarball. Whatever remains still
70
+ # came from `npm pack`, so the bundle is a subset of the published package and
71
+ # never a file npm does not ship.
72
+ ( cd "$APP/Contents/Resources/cli/package" \
73
+ && rm -rf docs skills examples scripts \
74
+ README.md CONTRIBUTING.md SECURITY.md CHANGELOG.md "Refresh & Open Dashboard.command" )
75
+ [ -f "$BUNDLED_CLI" ] || { echo "error: pruning removed the CLI" >&2; exit 1; }
76
+ [ -d "$APP/Contents/Resources/cli/package/src" ] || { echo "error: pruning removed src/" >&2; exit 1; }
77
+
36
78
  cat > "$APP/Contents/Info.plist" <<PLIST
37
79
  <?xml version="1.0" encoding="UTF-8"?>
38
80
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -50,12 +92,19 @@ cat > "$APP/Contents/Info.plist" <<PLIST
50
92
  <key>LSUIElement</key> <true/>
51
93
  <key>NSHighResolutionCapable</key> <true/>
52
94
  <key>NSHumanReadableCopyright</key> <string>MIT — local-first, nothing leaves your machine.</string>
53
- <key>TokenFlowNodePath</key> <string>$NODE_BIN</string>
54
- <key>TokenFlowCLIPath</key> <string>$CLI_JS</string>
55
95
  </dict>
56
96
  </plist>
57
97
  PLIST
58
98
 
99
+ # A distributable build embeds no machine-specific path. A local one does, so
100
+ # the developer's installed app drives the clone they are working in.
101
+ if [ "$PORTABLE" != "1" ]; then
102
+ /usr/libexec/PlistBuddy \
103
+ -c "Add :TokenFlowNodePath string $NODE_BIN" \
104
+ -c "Add :TokenFlowCLIPath string $CLI_JS" \
105
+ "$APP/Contents/Info.plist" >/dev/null
106
+ fi
107
+
59
108
  echo "compiling with $(swiftc --version | head -1)"
60
109
  swiftc -O -swift-version 5 \
61
110
  -o "$APP/Contents/MacOS/TokenFlow" \
@@ -63,5 +112,5 @@ swiftc -O -swift-version 5 \
63
112
 
64
113
  codesign --force --sign - "$APP" >/dev/null 2>&1 || true
65
114
 
66
- SIZE=$(du -h "$APP" | cut -f1 | tr -d ' ')
67
- echo "built: $APP ($SIZE)"
115
+ SIZE=$(du -sh "$APP" | cut -f1 | tr -d ' ')
116
+ echo "built: $APP ($SIZE)$([ "$PORTABLE" = "1" ] && echo ' · portable, no embedded paths')"