Sanitize repository public surface

This commit is contained in:
Esteban Suarez committed 2026-08-17 18:43:37 -03:00
1 parent ede05fa46e
commit 925eb2572e
20 files changed
+192 -36

No files matched your search

+5
View File
@@ -39,6 +39,10 @@ jobs:
if: matrix.optimize == 'Debug'
run: python3 -m scripts.pgso.corpus --manifest scripts/pgso/corpus.json --list
- name: Audit public surface
if: matrix.optimize == 'Debug'
run: ./scripts/check-public-surface.sh
- name: Build
run: zig build -Doptimize=${{ matrix.optimize }}
@@ -327,6 +331,7 @@ jobs:
run: |
scripts=(
benchmarks/startup.sh
scripts/check-public-surface.sh
)
bash -n "${scripts[@]}"
shellcheck "${scripts[@]}"
+4 -1
View File
@@ -48,7 +48,10 @@ jobs:
- name: Package
run: |
tar -czf fx-${{ matrix.name }}.tar.gz -C zig-out/bin fx
package_dir="$RUNNER_TEMP/fx-package-${{ matrix.name }}"
mkdir -p "$package_dir"
cp zig-out/bin/fx LICENSE THIRD_PARTY_NOTICES.md "$package_dir/"
tar -czf fx-${{ matrix.name }}.tar.gz -C "$package_dir" fx LICENSE THIRD_PARTY_NOTICES.md
sha256sum fx-${{ matrix.name }}.tar.gz > fx-${{ matrix.name }}.tar.gz.sha256
- name: Upload artifact
+4
View File
@@ -48,6 +48,10 @@ jobs:
if: matrix.optimize == 'Debug'
run: zig fmt --check src/
- name: Audit public surface
if: matrix.optimize == 'Debug'
run: ./scripts/check-public-surface.sh
- name: Build
run: zig build -Doptimize=${{ matrix.optimize }}
+4 -1
View File
@@ -460,7 +460,10 @@ jobs:
run: |
python3 -m scripts.pgso report --output-dir "$RUNNER_TEMP/fx-pgso-aggregate" >/dev/null
archive="$RUNNER_TEMP/fx-macos-aarch64.tar.gz"
tar -czf "$archive" -C "$RUNNER_TEMP/fx-pgso-aggregate/candidate" fx
package_dir="$RUNNER_TEMP/fx-release-package"
mkdir -p "$package_dir"
cp "$RUNNER_TEMP/fx-pgso-aggregate/candidate/fx" LICENSE THIRD_PARTY_NOTICES.md "$package_dir/"
tar -czf "$archive" -C "$package_dir" fx LICENSE THIRD_PARTY_NOTICES.md
cd "$RUNNER_TEMP"
shasum -a 256 fx-macos-aarch64.tar.gz > fx-macos-aarch64.tar.gz.sha256
+4 -1
View File
@@ -70,7 +70,10 @@ jobs:
- name: Package
# sha256sum is available on all ubuntu runners used in this workflow
run: |
tar -czf fx-${{ matrix.name }}.tar.gz -C zig-out/bin fx
package_dir="$RUNNER_TEMP/fx-package-${{ matrix.name }}"
mkdir -p "$package_dir"
cp zig-out/bin/fx LICENSE THIRD_PARTY_NOTICES.md "$package_dir/"
tar -czf fx-${{ matrix.name }}.tar.gz -C "$package_dir" fx LICENSE THIRD_PARTY_NOTICES.md
sha256sum fx-${{ matrix.name }}.tar.gz > fx-${{ matrix.name }}.tar.gz.sha256
- name: Upload artifact
+3
View File
@@ -97,6 +97,9 @@ Run the test suite with `zig build test`. See [CONTRIBUTING.md](CONTRIBUTING.md)
[Apache-2.0](LICENSE)
Third-party licenses and attributions are listed in
[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
## Credits
Interface sounds by [cuelume](https://github.com/Danilaa1/cuelume).
+73
View File
@@ -0,0 +1,73 @@
# Third-party notices
fx includes material from the following projects and data sources.
## cuelume
The embedded interface sounds are derived from the sound recipes in
[cuelume](https://github.com/Danilaa1/cuelume) by Daniel Belyi.
MIT License
Copyright (c) 2026 Daniel Belyi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## Unicode
The generated Unicode lookup tables in fx use data published by the Unicode
Consortium.
UNICODE LICENSE V3
COPYRIGHT AND PERMISSION NOTICE
Copyright © 1991-2026 Unicode, Inc.
NOTICE TO USER: Carefully read the following legal agreement. BY DOWNLOADING,
INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR SOFTWARE, YOU
UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS
OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY,
DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
Permission is hereby granted, free of charge, to any person obtaining a copy
of data files and any associated documentation (the "Data Files") or software
and any associated documentation (the "Software") to deal in the Data Files
or Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, and/or sell copies of the Data
Files or Software, and to permit persons to whom the Data Files or Software are
furnished to do so, provided that either (a) this copyright and permission
notice appear with all copies of the Data Files or Software, or (b) this
copyright and permission notice appear in associated Documentation.
THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD
PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE
LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR SOFTWARE.
Except as contained in this notice, the name of a copyright holder shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in these Data Files or Software without prior written authorization of the
copyright holder.
+2 -3
View File
@@ -43,9 +43,8 @@
.paths = .{
"build.zig",
"build.zig.zon",
"LICENSE",
"src",
// For example...
//"LICENSE",
//"README.md",
"THIRD_PARTY_NOTICES.md",
},
}
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(git rev-parse --show-toplevel)"
cd "$repo_root"
personal_paths="$({
git grep -n -E '/Users/[^/[:space:]]+/' -- . ':(exclude)scripts/check-public-surface.sh' || true
} | grep -Ev '/Users/(example|tester|private|me|<you>)/' || true)"
if [[ -n "$personal_paths" ]]; then
printf 'Tracked personal paths found:\n%s\n' "$personal_paths" >&2
exit 1
fi
real_team_ids="$({
git grep -n -E 'team_[A-Za-z0-9]{24}' -- . ':(exclude)scripts/check-public-surface.sh' || true
} | grep -Fv 'team_000000000000000000000000' || true)"
if [[ -n "$real_team_ids" ]]; then
printf 'Tracked real-looking team identifiers found:\n%s\n' "$real_team_ids" >&2
exit 1
fi
internal_slugs="$(git grep -n -E 'vercel-internal-[a-z0-9-]+' -- . ':(exclude)scripts/check-public-surface.sh' || true)"
if [[ -n "$internal_slugs" ]]; then
printf 'Tracked internal team slugs found:\n%s\n' "$internal_slugs" >&2
exit 1
fi
personal_project_slugs="$(git grep -n -E '[a-z]+-[0-9]+s-projects' -- . ':(exclude)scripts/check-public-surface.sh' || true)"
if [[ -n "$personal_project_slugs" ]]; then
printf 'Tracked personal project slugs found:\n%s\n' "$personal_project_slugs" >&2
exit 1
fi
capture='tests/e2e/fixtures/fx-render-bug-20260510-075848.tar.gz'
archive_listing="$(tar -tzvf "$capture")"
unexpected_owners="$(grep -Ev '[[:space:]]root([/]|[[:space:]]+)root[[:space:]]' <<<"$archive_listing" || true)"
if [[ -n "$unexpected_owners" ]]; then
printf 'Render fixture archive metadata contains a non-root owner:\n%s\n' "$unexpected_owners" >&2
exit 1
fi
if grep -Eq '/\._' <<<"$archive_listing"; then
printf 'Render fixture contains macOS AppleDouble metadata\n' >&2
exit 1
fi
extract_dir="$(mktemp -d)"
trap 'rm -rf "$extract_dir"' EXIT
tar -xzf "$capture" -C "$extract_dir"
archive_matches="$({
grep -R -a -n -E '/Users/[^/[:space:]]+/|team_[A-Za-z0-9]{24}' "$extract_dir" || true
} | grep -Ev '/Users/(guest|example|tester|private|me)/|team_000000000000000000000000' || true)"
if [[ -n "$archive_matches" ]]; then
printf 'Render fixture contains personal or internal data:\n%s\n' "$archive_matches" >&2
exit 1
fi
if ! grep -R -a -q 'sanitized-v1' "$extract_dir"; then
printf 'Render fixture is missing its sanitized branch marker\n' >&2
exit 1
fi
+2 -2
View File
@@ -1653,8 +1653,8 @@ fn stubFetchForbiddenCredits(
test "built-in credits provider names the team query only when valid" {
const cases = [_]struct { team: ?[]const u8, want: []const u8 }{
.{ .team = null, .want = "/coding-agent/v1/credits" },
.{ .team = "team_ut02N41HAM44llPMehBfFdqY", .want = "/coding-agent/v1/credits?teamId=team_ut02N41HAM44llPMehBfFdqY" },
.{ .team = "pranit-2381s-projects", .want = "/coding-agent/v1/credits?teamId=pranit-2381s-projects" },
.{ .team = "team_000000000000000000000000", .want = "/coding-agent/v1/credits?teamId=team_000000000000000000000000" },
.{ .team = "example-team", .want = "/coding-agent/v1/credits?teamId=example-team" },
.{ .team = "team a/../b", .want = "/coding-agent/v1/credits" },
.{ .team = "", .want = "/coding-agent/v1/credits" },
};
+6 -6
View File
@@ -2095,21 +2095,21 @@ test "change team search filters by name and slug without losing original indexe
&selection,
alloc,
"team_123",
"vercel-internal-playground",
"Internal Playground",
"example-internal-team",
"Example Internal Team",
);
try appendTestTeam(&selection, alloc, "team_789", "zero-conf", "Zero Conf");
runtime.openTeamPicker(alloc, &selection);
for ("PLAY") |byte| try std.testing.expect(try runtime.appendTeamQueryByte(alloc, byte));
for ("EXAMPLE") |byte| try std.testing.expect(try runtime.appendTeamQueryByte(alloc, byte));
const name_match = runtime.pickerView();
try std.testing.expectEqualStrings("PLAY", name_match.team_query);
try std.testing.expectEqualStrings("EXAMPLE", name_match.team_query);
try std.testing.expectEqual(@as(usize, 1), name_match.choiceCount());
try std.testing.expect((Choice{ .team = 1 }).eql(name_match.choiceAt(0).?));
try std.testing.expectEqualStrings("Internal Playground", name_match.choiceLabel(name_match.choiceAt(0).?));
try std.testing.expectEqualStrings("Example Internal Team", name_match.choiceLabel(name_match.choiceAt(0).?));
for (0..4) |_| try std.testing.expect(runtime.deleteTeamQueryByte());
for (0..7) |_| try std.testing.expect(runtime.deleteTeamQueryByte());
for ("zero-conf") |byte| try std.testing.expect(try runtime.appendTeamQueryByte(alloc, byte));
const slug_match = runtime.pickerView();
try std.testing.expectEqual(@as(usize, 1), slug_match.choiceCount());
+3 -3
View File
@@ -1793,7 +1793,7 @@ test "core status snapshot text and json stay stable" {
test "core status snapshot includes selected team when present" {
const snapshot = StatusSnapshot{
.model = "alpha",
.auth = .{ .active_source = .fx_login, .team = "estebans" },
.auth = .{ .active_source = .fx_login, .team = "example-team" },
.permission_mode = .ask,
.workspace_root = "/tmp/fx",
.history_turns = 0,
@@ -1804,14 +1804,14 @@ test "core status snapshot includes selected team when present" {
const text = try snapshot.renderText(std.testing.allocator);
defer std.testing.allocator.free(text);
try std.testing.expectEqualStrings(
"[status] model=alpha\n[status] update_channel=stable\n[status] build_channel=stable\n[status] auth=fx login\n[status] auth_refreshable=true\n[status] team=estebans\n[status] permission_mode=ask\n[status] sandbox=none\n[status] workspace=/tmp/fx\n[status] history_turns=0\n[status] session_permission_grants=0\n[status] agent_step_limit=24\n",
"[status] model=alpha\n[status] update_channel=stable\n[status] build_channel=stable\n[status] auth=fx login\n[status] auth_refreshable=true\n[status] team=example-team\n[status] permission_mode=ask\n[status] sandbox=none\n[status] workspace=/tmp/fx\n[status] history_turns=0\n[status] session_permission_grants=0\n[status] agent_step_limit=24\n",
text,
);
const json = try snapshot.renderJson(std.testing.allocator);
defer std.testing.allocator.free(json);
try std.testing.expectEqualStrings(
"{\"kind\":\"status\",\"model\":\"alpha\",\"update_channel\":\"stable\",\"build_channel\":\"stable\",\"build_revision\":\"\",\"auth\":\"fx login\",\"auth_refreshable\":true,\"team\":\"estebans\",\"permission_mode\":\"ask\",\"sandbox\":\"none\",\"workspace\":\"/tmp/fx\",\"history_turns\":0,\"session_permission_grants\":0,\"agent_step_limit\":24}",
"{\"kind\":\"status\",\"model\":\"alpha\",\"update_channel\":\"stable\",\"build_channel\":\"stable\",\"build_revision\":\"\",\"auth\":\"fx login\",\"auth_refreshable\":true,\"team\":\"example-team\",\"permission_mode\":\"ask\",\"sandbox\":\"none\",\"workspace\":\"/tmp/fx\",\"history_turns\":0,\"session_permission_grants\":0,\"agent_step_limit\":24}",
json,
);
}
@@ -1115,7 +1115,7 @@ fn testUsageTotals(tokens: u64, cost: f64) usage_report.Totals {
test "workspace menu keeps paths and status on the same width-safe row" {
var entries = [_]workspace_access.Entry{.{
.path = @constCast("/Users/faxes/Developer/Fx/docs"),
.path = @constCast("/Users/example/Developer/Fx/docs"),
.saved = true,
.command_line = false,
.available = true,
@@ -1124,7 +1124,7 @@ test "workspace menu keeps paths and status on the same width-safe row" {
const projection: CompactCommandMenuProjection = .{ .workspace = .{
.active = true,
.selected_row = 1,
.primary_directory = "/Users/faxes/Developer/Fx",
.primary_directory = "/Users/example/Developer/Fx",
.entries = &entries,
} };
@@ -1132,12 +1132,12 @@ test "workspace menu keeps paths and status on the same width-safe row" {
var primary = try composeCompactCommandMenuRow(std.testing.allocator, projection, 2, 8, 120);
defer primary.deinit(std.testing.allocator);
try std.testing.expect(std.mem.find(u8, primary.items, "Primary") != null);
try std.testing.expect(std.mem.find(u8, primary.items, "/Users/faxes/Developer/Fx") != null);
try std.testing.expect(std.mem.find(u8, primary.items, "/Users/example/Developer/Fx") != null);
var entry = try composeCompactCommandMenuRow(std.testing.allocator, projection, 6, 8, 120);
defer entry.deinit(std.testing.allocator);
try std.testing.expect(std.mem.findScalar(u8, entry.items, '\n') == null);
try std.testing.expect(std.mem.find(u8, entry.items, "/Users/faxes/Developer/Fx/docs") != null);
try std.testing.expect(std.mem.find(u8, entry.items, "/Users/example/Developer/Fx/docs") != null);
try std.testing.expect(std.mem.find(u8, entry.items, "Active · Saved") != null);
try std.testing.expect(display_width.visibleWidthIgnoringAnsi(entry.items) <= 120);
}
+2 -2
View File
@@ -1668,10 +1668,10 @@ test "auth picker renders the staged switch and disabled team screens" {
defer search_header.deinit(alloc);
try std.testing.expect(std.mem.find(u8, search_header.items, "Search: play") != null);
search_view.team_query = "vercel-internal-playground";
search_view.team_query = "example-internal-team";
var narrow_search_header = try composeAuthPickerRow(alloc, search_view, 0, 2, 20);
defer narrow_search_header.deinit(alloc);
try std.testing.expect(std.mem.find(u8, narrow_search_header.items, "playground") != null);
try std.testing.expect(std.mem.find(u8, narrow_search_header.items, "nternal-team") != null);
try std.testing.expectEqual(
@as(u16, 20),
authPickerQueryCursorColumn(search_view, 20).?,
+2 -2
View File
@@ -540,7 +540,7 @@ test "resume menu renders each session on one line with a right metadata cluster
const alloc = std.testing.allocator;
const summaries = [_]session_store.SessionSummary{.{
.id = @constCast("one"),
.workspace_root = @constCast("/Users/faxes/Developer/Fx/worktrees/resume-catalog"),
.workspace_root = @constCast("/Users/example/Developer/Fx/worktrees/resume-catalog"),
.title = @constCast("Redesign resume menu"),
.created_at_ms = 1,
.updated_at_ms = 1_000,
@@ -705,7 +705,7 @@ test "resume menu keeps long titles on one narrow row without a workspace spill
const alloc = std.testing.allocator;
const summaries = [_]session_store.SessionSummary{.{
.id = @constCast("one"),
.workspace_root = @constCast("/Users/faxes/Developer/Fx/worktrees/a-very-long-session-catalog-worktree"),
.workspace_root = @constCast("/Users/example/Developer/Fx/worktrees/a-very-long-session-catalog-worktree"),
.title = @constCast("Investigate a very long session catalog rendering issue"),
.created_at_ms = 1,
.updated_at_ms = 1,
+2 -2
View File
@@ -1728,8 +1728,8 @@ test "surface footer places the cursor after the Vercel team query" {
var shell = surfaceTestShell(24, 80);
defer shell.deinit(alloc);
var team_id = "team_123".*;
var team_slug = "vercel-internal-playground".*;
var team_name = "Internal Playground".*;
var team_slug = "example-internal-team".*;
var team_name = "Example Internal Team".*;
const teams = [_]login_flow.Team{.{
.id = &team_id,
.slug = &team_slug,
+1 -1
View File
@@ -130,7 +130,7 @@ test "resume screen places composer catalog and controls without transcript chro
const session_store = @import("../core/session/session_store.zig");
const sessions = [_]session_store.SessionSummary{.{
.id = @constCast("one"),
.workspace_root = @constCast("/Users/faxes/Developer/Fx/worktrees/resume-catalog"),
.workspace_root = @constCast("/Users/example/Developer/Fx/worktrees/resume-catalog"),
.title = @constCast("Redesign resume menu"),
.created_at_ms = 1,
.updated_at_ms = 1_000,
+3 -3
View File
@@ -78,7 +78,7 @@ It drives one tmux shell through this shape:
Command:
```bash
cd /Users/faxes/Developer/Fx/fx-worktree-rendering
cd /Users/example/Developer/Fx/fx-worktree-rendering
zig build
cd tests/e2e
bun run render-lab -- --scenario same-shell-relaunch --runs 1 --out /private/tmp/fx-render-lab
@@ -87,7 +87,7 @@ bun run render-lab -- --scenario same-shell-relaunch --runs 1 --out /private/tmp
Default test wrapper:
```bash
cd /Users/faxes/Developer/Fx/fx-worktree-rendering/tests/e2e
cd /Users/example/Developer/Fx/fx-worktree-rendering/tests/e2e
bun test tui-render-lab.test.ts
```
@@ -98,7 +98,7 @@ This wrapper is CI-safe apart from requiring tmux. It does not require `AI_GATEW
List available scenarios:
```bash
cd /Users/faxes/Developer/Fx/fx-worktree-rendering/tests/e2e
cd /Users/example/Developer/Fx/fx-worktree-rendering/tests/e2e
bun run render-lab -- --list-scenarios
```
+5 -5
View File
@@ -86,7 +86,7 @@ function writeSeededFxLogin(
};
if (teamId) {
auth.team_id = teamId;
auth.team_slug = "vercel-internal-playground";
auth.team_slug = "example-internal-team";
}
writeFileSync(authPath, JSON.stringify(auth) + "\n", { mode: 0o600 });
chmodSync(authPath, 0o600);
@@ -802,7 +802,7 @@ tmuxTest(
{
teams: [
{ id: "team_456", slug: "other-team", name: "Other Team" },
{ id: "team_123", slug: "vercel-internal-playground", name: "Internal Playground" },
{ id: "team_123", slug: "example-internal-team", name: "Example Internal Team" },
],
},
);
@@ -840,7 +840,7 @@ tmuxTest(
(pane) =>
pane.includes("Choose a Vercel team") &&
pane.includes("Search: play") &&
pane.includes("Internal Playground") &&
pane.includes("Example Internal Team") &&
!pane.includes("Other Team"),
TIMEOUT,
);
@@ -874,7 +874,7 @@ tmuxTest(
expect(catalogEvents.at(-1)).toContain(
"requested_access=authenticated credential_source=fx_login effective_access=authenticated",
);
for (const secret of [ACQUIRED_LOGIN_TOKEN, "team_123", "vercel-internal-playground"]) {
for (const secret of [ACQUIRED_LOGIN_TOKEN, "team_123", "example-internal-team"]) {
expect(trace).not.toContain(secret);
}
expect(readFileSync(stderrPath, "utf8")).toBe("");
@@ -1550,7 +1550,7 @@ tmuxTest(
"seeded-refresh-token",
"acquired-refresh-token",
"team_123",
"vercel-internal-playground",
"example-internal-team",
]) {
expect(trace).not.toContain(secret);
}