mirror of
https://github.com/block/buzz.git
synced 2026-10-29 21:41:33 +00:00
feat(desktop): exclude Bestie from OSS artifacts
Signed-off-by: Arjun Mahanti <arjun@squareup.com> Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -206,7 +206,8 @@ RUST_LOG=buzz_relay=debug,buzz_datastore=info,buzz_db=debug,buzz_auth=debug,buzz
|
||||
# app launch while keeping the current identity and relay data.
|
||||
# VITE_BUZZ_FORCE_FRESH_ONBOARDING=true
|
||||
|
||||
# Makes the default-off Bestie experiment available in eligible builds.
|
||||
# Protected internal builds only: selects the module graph that contains the
|
||||
# default-off Bestie experiment. Official OSS builds must leave this unset.
|
||||
# VITE_BUZZ_BESTIE=1
|
||||
|
||||
# ── Subscription & filtering ─────────────────────────────────────────────────
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"build": "tsc && node ./scripts/build-protected-feature-artifacts.mjs",
|
||||
"build:e2e": "tsc && vite build --mode e2e",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"check:file-sizes": "node ./scripts/check-file-sizes.mjs",
|
||||
|
||||
108
desktop/scripts/build-protected-feature-artifacts.mjs
Normal file
108
desktop/scripts/build-protected-feature-artifacts.mjs
Normal file
@@ -0,0 +1,108 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import {
|
||||
mkdtempSync,
|
||||
readFileSync,
|
||||
readdirSync,
|
||||
rmSync,
|
||||
statSync,
|
||||
} from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const desktopRoot = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
"..",
|
||||
);
|
||||
const selectedInternalVariant = process.env.VITE_BUZZ_BESTIE === "1";
|
||||
const scratchRoot = mkdtempSync(
|
||||
path.join(tmpdir(), "buzz-protected-feature-artifacts-"),
|
||||
);
|
||||
const selectedOutput = path.join(desktopRoot, "dist");
|
||||
const alternateOutput = path.join(scratchRoot, "alternate");
|
||||
const viteBinary = process.platform === "win32" ? "vite.cmd" : "vite";
|
||||
|
||||
function buildVariant({ internal, output }) {
|
||||
const env = { ...process.env };
|
||||
if (internal) {
|
||||
env.VITE_BUZZ_BESTIE = "1";
|
||||
} else {
|
||||
delete env.VITE_BUZZ_BESTIE;
|
||||
}
|
||||
|
||||
const result = spawnSync(
|
||||
viteBinary,
|
||||
["build", "--outDir", output, "--emptyOutDir"],
|
||||
{
|
||||
cwd: desktopRoot,
|
||||
env,
|
||||
stdio: "inherit",
|
||||
},
|
||||
);
|
||||
if (result.error) throw result.error;
|
||||
if (result.status !== 0) {
|
||||
throw new Error(
|
||||
`${internal ? "internal" : "OSS"} desktop build failed with status ${result.status}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function emittedText(root) {
|
||||
const chunks = [];
|
||||
const visit = (candidate) => {
|
||||
const stat = statSync(candidate);
|
||||
if (stat.isDirectory()) {
|
||||
for (const child of readdirSync(candidate)) {
|
||||
visit(path.join(candidate, child));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (/\.(?:css|html|js|json)$/u.test(candidate)) {
|
||||
chunks.push(readFileSync(candidate, "utf8"));
|
||||
}
|
||||
};
|
||||
visit(root);
|
||||
return chunks.join("\n");
|
||||
}
|
||||
|
||||
function assertArtifactContract({ ossOutput, internalOutput }) {
|
||||
const ossText = emittedText(ossOutput);
|
||||
const internalText = emittedText(internalOutput);
|
||||
const protectedContent = /\bbestie\b|chief of staff|builtin:bestie/iu;
|
||||
const internalManifestMarker =
|
||||
"Try a personal agent that is always close at hand";
|
||||
|
||||
if (protectedContent.test(ossText)) {
|
||||
throw new Error(
|
||||
"Official OSS desktop artifact contains protected Bestie/Chief content",
|
||||
);
|
||||
}
|
||||
if (!internalText.includes(internalManifestMarker)) {
|
||||
throw new Error(
|
||||
"Protected internal desktop artifact is missing the Bestie manifest",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Build the unselected variant outside dist first, then leave the requested
|
||||
// variant in dist for Vite/Tauri's ordinary packaging contract.
|
||||
buildVariant({
|
||||
internal: !selectedInternalVariant,
|
||||
output: alternateOutput,
|
||||
});
|
||||
buildVariant({
|
||||
internal: selectedInternalVariant,
|
||||
output: selectedOutput,
|
||||
});
|
||||
|
||||
assertArtifactContract({
|
||||
ossOutput: selectedInternalVariant ? alternateOutput : selectedOutput,
|
||||
internalOutput: selectedInternalVariant ? selectedOutput : alternateOutput,
|
||||
});
|
||||
console.log(
|
||||
`Protected feature artifact matrix passed; dist contains the ${selectedInternalVariant ? "internal" : "OSS"} variant.`,
|
||||
);
|
||||
} finally {
|
||||
rmSync(scratchRoot, { recursive: true, force: true });
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { setAgentManagedProfiles } from "@/shared/api/tauri";
|
||||
import { availableDesktopFeatures, useFeatureToggle } from "@/shared/features";
|
||||
import { desktopFeatures, useFeatureToggle } from "@/shared/features";
|
||||
import type { FeatureDefinition } from "@/shared/features";
|
||||
import { Switch } from "@/shared/ui/switch";
|
||||
import { SettingsOptionGroup, SettingsOptionRow } from "./SettingsOptionGroup";
|
||||
@@ -42,7 +42,7 @@ function FeatureRow({ feature }: { feature: FeatureDefinition }) {
|
||||
export function ExperimentalFeaturesCard() {
|
||||
// Manifest is preview-only by definition; every desktop entry is a preview
|
||||
// feature.
|
||||
const previewFeatures = availableDesktopFeatures;
|
||||
const previewFeatures = desktopFeatures;
|
||||
|
||||
return (
|
||||
<section className="min-w-0" data-testid="settings-experimental">
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from "@/shared/api/relayMembers";
|
||||
import {
|
||||
getFeature,
|
||||
resolveFeatureEnabledInThisBuild,
|
||||
resolveEnabled,
|
||||
useFeatureSnapshot,
|
||||
} from "@/shared/features";
|
||||
import { topChromeBackdrop } from "@/shared/layout/chromeLayout";
|
||||
@@ -139,7 +139,7 @@ export function SettingsView({
|
||||
const feature = getFeature(s.featureGate);
|
||||
if (
|
||||
feature &&
|
||||
!resolveFeatureEnabledInThisBuild(feature, featureState)
|
||||
!resolveEnabled(s.featureGate, featureState, feature.defaultEnabled)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
11
desktop/src/protectedFeatures/internal.ts
Normal file
11
desktop/src/protectedFeatures/internal.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { FeatureDefinition } from "@/shared/features/types";
|
||||
|
||||
/** Definitions available only in the protected internal application build. */
|
||||
export const protectedFeatureDefinitions: FeatureDefinition[] = [
|
||||
{
|
||||
id: "bestie",
|
||||
name: "Bestie",
|
||||
description: "Try a personal agent that is always close at hand",
|
||||
platforms: ["desktop"],
|
||||
},
|
||||
];
|
||||
19
desktop/src/protectedFeatures/protectedFeatures.test.mjs
Normal file
19
desktop/src/protectedFeatures/protectedFeatures.test.mjs
Normal file
@@ -0,0 +1,19 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { describe, it } from "node:test";
|
||||
|
||||
import { protectedFeatureDefinitions as internalDefinitions } from "./internal.ts";
|
||||
import { protectedFeatureDefinitions as publicDefinitions } from "./public.ts";
|
||||
|
||||
describe("protected feature build variants", () => {
|
||||
it("keeps protected definitions out of the OSS module", () => {
|
||||
assert.deepEqual(publicDefinitions, []);
|
||||
});
|
||||
|
||||
it("adds Bestie only through the internal module", () => {
|
||||
assert.deepEqual(
|
||||
internalDefinitions.map((feature) => feature.id),
|
||||
["bestie"],
|
||||
);
|
||||
assert.equal(internalDefinitions[0]?.defaultEnabled, undefined);
|
||||
});
|
||||
});
|
||||
7
desktop/src/protectedFeatures/public.ts
Normal file
7
desktop/src/protectedFeatures/public.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { FeatureDefinition } from "@/shared/features/types";
|
||||
|
||||
/**
|
||||
* Protected feature definitions compiled into the official OSS application.
|
||||
* Keep this module free of protected product names, metadata, and imports.
|
||||
*/
|
||||
export const protectedFeatureDefinitions: FeatureDefinition[] = [];
|
||||
@@ -1,50 +0,0 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { describe, it } from "node:test";
|
||||
|
||||
import {
|
||||
filterBuildAvailableFeatures,
|
||||
isFeatureBuildAvailable,
|
||||
} from "./buildAvailability.ts";
|
||||
|
||||
const publicFeature = {
|
||||
id: "projects",
|
||||
name: "Projects",
|
||||
description: "",
|
||||
};
|
||||
const bestie = {
|
||||
id: "bestie",
|
||||
name: "Bestie",
|
||||
description: "",
|
||||
requiredBuildFlag: "bestie",
|
||||
};
|
||||
|
||||
describe("feature build availability", () => {
|
||||
it("keeps features without a required build flag available", () => {
|
||||
assert.equal(
|
||||
isFeatureBuildAvailable(publicFeature, { bestie: false }),
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it("does not allow a required feature into an ineligible build", () => {
|
||||
assert.equal(isFeatureBuildAvailable(bestie, { bestie: false }), false);
|
||||
});
|
||||
|
||||
it("shows a required feature when its build opts in", () => {
|
||||
assert.equal(isFeatureBuildAvailable(bestie, { bestie: true }), true);
|
||||
});
|
||||
|
||||
it("filters unavailable features out of the experiment picker", () => {
|
||||
assert.deepEqual(
|
||||
filterBuildAvailableFeatures([publicFeature, bestie], { bestie: false }),
|
||||
[publicFeature],
|
||||
);
|
||||
});
|
||||
|
||||
it("includes the Bestie experiment only in an eligible build", () => {
|
||||
assert.deepEqual(
|
||||
filterBuildAvailableFeatures([publicFeature, bestie], { bestie: true }),
|
||||
[publicFeature, bestie],
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
import type { FeatureBuildFlag, FeatureDefinition } from "./types";
|
||||
|
||||
export type FeatureBuildFlags = Record<FeatureBuildFlag, boolean>;
|
||||
|
||||
/**
|
||||
* Build capabilities are deliberately separate from user preference. A local
|
||||
* storage override cannot make an unavailable feature visible.
|
||||
*/
|
||||
export function isFeatureBuildAvailable(
|
||||
feature: FeatureDefinition,
|
||||
buildFlags: FeatureBuildFlags,
|
||||
): boolean {
|
||||
const requiredFlag = feature.requiredBuildFlag;
|
||||
return requiredFlag ? buildFlags[requiredFlag] : true;
|
||||
}
|
||||
|
||||
export function filterBuildAvailableFeatures(
|
||||
features: FeatureDefinition[],
|
||||
buildFlags: FeatureBuildFlags,
|
||||
): FeatureDefinition[] {
|
||||
return features.filter((feature) =>
|
||||
isFeatureBuildAvailable(feature, buildFlags),
|
||||
);
|
||||
}
|
||||
@@ -1,13 +1,8 @@
|
||||
export { FeatureGate } from "./FeatureGate";
|
||||
export { allFeatures, desktopFeatures, getFeature, manifest } from "./manifest";
|
||||
export {
|
||||
availableDesktopFeatures,
|
||||
resolveFeatureEnabledInThisBuild,
|
||||
} from "./runtimeBuildAvailability";
|
||||
export { getOverrides, setOverride, clearOverride } from "./store";
|
||||
export type {
|
||||
FeatureDefinition,
|
||||
FeatureBuildFlag,
|
||||
FeaturesManifest,
|
||||
FeaturePlatform,
|
||||
} from "./types";
|
||||
@@ -16,4 +11,5 @@ export {
|
||||
useFeatureToggle,
|
||||
useFeatureSnapshot,
|
||||
usePreviewFeatureWarning,
|
||||
resolveEnabled,
|
||||
} from "./useFeatureEnabled";
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import manifestJson from "@features-manifest";
|
||||
import { protectedFeatureDefinitions } from "@protected-features";
|
||||
import { z } from "zod";
|
||||
import type { FeatureDefinition, FeaturesManifest } from "./types";
|
||||
|
||||
@@ -8,7 +9,6 @@ import type { FeatureDefinition, FeaturesManifest } from "./types";
|
||||
// The app keeps working; gated UI stays hidden; nothing accidentally leaks.
|
||||
|
||||
const FeaturePlatformSchema = z.enum(["desktop", "mobile"]);
|
||||
const FeatureBuildFlagSchema = z.enum(["bestie"]);
|
||||
|
||||
const FeatureDefinitionSchema = z.object({
|
||||
id: z.string().min(1),
|
||||
@@ -16,7 +16,6 @@ const FeatureDefinitionSchema = z.object({
|
||||
description: z.string(),
|
||||
defaultEnabled: z.boolean().optional(),
|
||||
platforms: z.array(FeaturePlatformSchema).optional(),
|
||||
requiredBuildFlag: FeatureBuildFlagSchema.optional(),
|
||||
});
|
||||
|
||||
const FeaturesManifestSchema = z.object({
|
||||
@@ -27,7 +26,10 @@ const FeaturesManifestSchema = z.object({
|
||||
const EMPTY_MANIFEST: FeaturesManifest = { version: 1, features: [] };
|
||||
|
||||
function loadManifest(): FeaturesManifest {
|
||||
const result = FeaturesManifestSchema.safeParse(manifestJson);
|
||||
const result = FeaturesManifestSchema.safeParse({
|
||||
...manifestJson,
|
||||
features: [...manifestJson.features, ...protectedFeatureDefinitions],
|
||||
});
|
||||
if (!result.success) {
|
||||
console.warn(
|
||||
"[FeatureFlags] preview-features.json failed schema validation; falling back to empty manifest.",
|
||||
|
||||
@@ -4,74 +4,30 @@ import { describe, it } from "node:test";
|
||||
import { resolveEnabled } from "./resolveEnabled.ts";
|
||||
|
||||
describe("resolveEnabled (preview-only)", () => {
|
||||
const workflows = {
|
||||
id: "workflows",
|
||||
name: "Workflows",
|
||||
description: "",
|
||||
};
|
||||
|
||||
it("returns false by default (no override)", () => {
|
||||
assert.equal(resolveEnabled(workflows, {}, true), false);
|
||||
assert.equal(resolveEnabled("workflows", {}), false);
|
||||
});
|
||||
|
||||
it("returns true when user opts in", () => {
|
||||
assert.equal(resolveEnabled(workflows, { workflows: true }, true), true);
|
||||
assert.equal(resolveEnabled("workflows", { workflows: true }), true);
|
||||
});
|
||||
|
||||
it("uses an enabled manifest default when no override exists", () => {
|
||||
assert.equal(
|
||||
resolveEnabled(
|
||||
{ ...workflows, id: "defaultOnFeature", defaultEnabled: true },
|
||||
{},
|
||||
true,
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(resolveEnabled("defaultOnFeature", {}, true), true);
|
||||
});
|
||||
|
||||
it("lets an explicit opt-out override an enabled default", () => {
|
||||
assert.equal(
|
||||
resolveEnabled(
|
||||
{ ...workflows, id: "defaultOnFeature", defaultEnabled: true },
|
||||
{ defaultOnFeature: false },
|
||||
true,
|
||||
),
|
||||
resolveEnabled("defaultOnFeature", { defaultOnFeature: false }, true),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it("returns false when user explicitly opts out", () => {
|
||||
assert.equal(resolveEnabled(workflows, { workflows: false }, true), false);
|
||||
assert.equal(resolveEnabled("workflows", { workflows: false }), false);
|
||||
});
|
||||
|
||||
it("ignores overrides for unrelated ids", () => {
|
||||
assert.equal(resolveEnabled(workflows, { pulse: true }, true), false);
|
||||
});
|
||||
|
||||
it("requires both build availability and a user opt-in", () => {
|
||||
const cases = [
|
||||
{ buildAvailable: false, optedIn: false, expected: false },
|
||||
{ buildAvailable: false, optedIn: true, expected: false },
|
||||
{ buildAvailable: true, optedIn: false, expected: false },
|
||||
{ buildAvailable: true, optedIn: true, expected: true },
|
||||
];
|
||||
|
||||
for (const { buildAvailable, optedIn, expected } of cases) {
|
||||
assert.equal(
|
||||
resolveEnabled(
|
||||
{ ...workflows, id: "bestie" },
|
||||
{ bestie: optedIn },
|
||||
buildAvailable,
|
||||
),
|
||||
expected,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("fails closed when a JavaScript caller omits build availability", () => {
|
||||
assert.equal(
|
||||
resolveEnabled({ ...workflows, id: "bestie" }, { bestie: true }),
|
||||
false,
|
||||
);
|
||||
assert.equal(resolveEnabled("workflows", { pulse: true }), false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,26 +1,19 @@
|
||||
/**
|
||||
* Pure resolution logic for preview-feature visibility.
|
||||
* No side effects — safe to test in isolation.
|
||||
* No side effects, no imports — safe to test in isolation.
|
||||
*
|
||||
* The manifest (`preview-features.json`) lists only preview features.
|
||||
* Anything not in the manifest is stable and resolves true elsewhere
|
||||
* (see `useFeatureEnabled`). Once you're inside `resolveEnabled`, the
|
||||
* feature IS in the manifest — preview by definition.
|
||||
*
|
||||
* Build availability is mandatory. Within an eligible build, an explicit user
|
||||
* override wins; otherwise the feature's manifest default is used (false when
|
||||
* omitted).
|
||||
* An explicit user override wins; otherwise the feature's manifest default is
|
||||
* used (false when omitted).
|
||||
*/
|
||||
import type { FeatureDefinition } from "./types";
|
||||
|
||||
export function resolveEnabled(
|
||||
feature: FeatureDefinition,
|
||||
featureId: string,
|
||||
overrides: Record<string, boolean>,
|
||||
buildAvailable: boolean,
|
||||
defaultEnabled = false,
|
||||
): boolean {
|
||||
// Strict equality is intentional: JavaScript callers that omit the
|
||||
// capability argument fail closed instead of recreating a build-gate side
|
||||
// door outside TypeScript.
|
||||
if (buildAvailable !== true) return false;
|
||||
return overrides[feature.id] ?? feature.defaultEnabled ?? false;
|
||||
return overrides[featureId] ?? defaultEnabled;
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import {
|
||||
filterBuildAvailableFeatures,
|
||||
isFeatureBuildAvailable,
|
||||
type FeatureBuildFlags,
|
||||
} from "./buildAvailability";
|
||||
import { desktopFeatures } from "./manifest";
|
||||
import { resolveEnabled } from "./resolveEnabled";
|
||||
import type { FeatureDefinition } from "./types";
|
||||
|
||||
const runtimeBuildFlags: FeatureBuildFlags = {
|
||||
bestie: import.meta.env.VITE_BUZZ_BESTIE === "1",
|
||||
};
|
||||
|
||||
export function isFeatureAvailableInThisBuild(
|
||||
feature: Parameters<typeof isFeatureBuildAvailable>[0],
|
||||
): boolean {
|
||||
return isFeatureBuildAvailable(feature, runtimeBuildFlags);
|
||||
}
|
||||
|
||||
/** Canonical preview resolution for the running build. */
|
||||
export function resolveFeatureEnabledInThisBuild(
|
||||
feature: FeatureDefinition,
|
||||
overrides: Record<string, boolean>,
|
||||
): boolean {
|
||||
return resolveEnabled(
|
||||
feature,
|
||||
overrides,
|
||||
isFeatureAvailableInThisBuild(feature),
|
||||
);
|
||||
}
|
||||
|
||||
export const availableDesktopFeatures = filterBuildAvailableFeatures(
|
||||
desktopFeatures,
|
||||
runtimeBuildFlags,
|
||||
);
|
||||
@@ -1,9 +1,6 @@
|
||||
/** Platforms a feature is available on */
|
||||
export type FeaturePlatform = "desktop" | "mobile";
|
||||
|
||||
/** Build-time capabilities that can make a preview available to users. */
|
||||
export type FeatureBuildFlag = "bestie";
|
||||
|
||||
/**
|
||||
* A single feature definition from the manifest.
|
||||
*
|
||||
@@ -19,8 +16,6 @@ export interface FeatureDefinition {
|
||||
defaultEnabled?: boolean;
|
||||
/** If omitted, feature is available on all platforms */
|
||||
platforms?: FeaturePlatform[];
|
||||
/** If present, the build must opt in before the user can see or enable it */
|
||||
requiredBuildFlag?: FeatureBuildFlag;
|
||||
}
|
||||
|
||||
/** The root manifest schema */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useSyncExternalStore, useCallback, useEffect } from "react";
|
||||
import { getFeature } from "./manifest";
|
||||
import { resolveFeatureEnabledInThisBuild } from "./runtimeBuildAvailability";
|
||||
import { resolveEnabled } from "./resolveEnabled";
|
||||
import { getOverrides, setOverride, OVERRIDES_KEY } from "./store";
|
||||
|
||||
type Listener = () => void;
|
||||
@@ -102,9 +102,11 @@ export function useFeatureEnabled(featureId: string): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
return resolveFeatureEnabledInThisBuild(feature, overrides);
|
||||
return resolveEnabled(featureId, overrides, feature.defaultEnabled);
|
||||
}
|
||||
|
||||
export { resolveEnabled } from "./resolveEnabled";
|
||||
|
||||
/**
|
||||
* Hook to toggle a feature override. Returns [enabled, toggle].
|
||||
*/
|
||||
|
||||
@@ -89,6 +89,12 @@ export function resolve(specifier, context, nextResolve) {
|
||||
const resolved = path.join(repoRoot, "preview-features.json");
|
||||
return nextResolve(toFileSpecifier(resolved), context);
|
||||
}
|
||||
if (specifier === "@protected-features") {
|
||||
const variant =
|
||||
process.env.VITE_BUZZ_BESTIE === "1" ? "internal.ts" : "public.ts";
|
||||
const resolved = path.join(srcRoot, "protectedFeatures", variant);
|
||||
return nextResolve(toFileSpecifier(resolved), context);
|
||||
}
|
||||
if (specifier === "@model-capabilities-manifest") {
|
||||
const resolved = path.join(repoRoot, "scripts", "model-capabilities.json");
|
||||
return nextResolve(toFileSpecifier(resolved), context);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"@features-manifest": ["../preview-features.json"],
|
||||
"@protected-features": ["./src/protectedFeatures/public.ts"],
|
||||
"@model-capabilities-manifest": ["../scripts/model-capabilities.json"]
|
||||
},
|
||||
|
||||
|
||||
@@ -1,56 +1,71 @@
|
||||
import path from "node:path";
|
||||
import { defineConfig } from "vite";
|
||||
import { defineConfig, loadEnv } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import { tanstackRouter } from "@tanstack/router-plugin/vite";
|
||||
|
||||
const host = process.env.TAURI_DEV_HOST;
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig(async () => ({
|
||||
plugins: [
|
||||
tanstackRouter({
|
||||
target: "react",
|
||||
routesDirectory: "./src/app/routes",
|
||||
generatedRouteTree: "./src/app/routeTree.gen.ts",
|
||||
virtualRouteConfig: "./src/app/routes.ts",
|
||||
quoteStyle: "double",
|
||||
semicolons: true,
|
||||
routeTreeFileHeader: [
|
||||
"// biome-ignore-all lint: generated by TanStack Router",
|
||||
],
|
||||
}),
|
||||
react(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": "/src",
|
||||
"@features-manifest": path.resolve(__dirname, "../preview-features.json"),
|
||||
"@model-capabilities-manifest": path.resolve(
|
||||
__dirname,
|
||||
"../scripts/model-capabilities.json",
|
||||
),
|
||||
},
|
||||
},
|
||||
export default defineConfig(async ({ mode }) => {
|
||||
const modeEnv = loadEnv(mode, __dirname, "");
|
||||
const protectedFeaturesEnabled =
|
||||
(process.env.VITE_BUZZ_BESTIE ?? modeEnv.VITE_BUZZ_BESTIE) === "1";
|
||||
|
||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
//
|
||||
// 1. prevent Vite from obscuring rust errors
|
||||
clearScreen: false,
|
||||
// 2. tauri expects a fixed port, fail if that port is not available
|
||||
server: {
|
||||
port: parseInt(process.env.VITE_PORT || "1420", 10),
|
||||
strictPort: true,
|
||||
host: host || false,
|
||||
hmr: host
|
||||
? {
|
||||
protocol: "ws",
|
||||
host,
|
||||
port: parseInt(process.env.VITE_HMR_PORT || "1421", 10),
|
||||
}
|
||||
: undefined,
|
||||
watch: {
|
||||
// 3. tell Vite to ignore watching `src-tauri`
|
||||
ignored: ["**/src-tauri/**"],
|
||||
return {
|
||||
plugins: [
|
||||
tanstackRouter({
|
||||
target: "react",
|
||||
routesDirectory: "./src/app/routes",
|
||||
generatedRouteTree: "./src/app/routeTree.gen.ts",
|
||||
virtualRouteConfig: "./src/app/routes.ts",
|
||||
quoteStyle: "double",
|
||||
semicolons: true,
|
||||
routeTreeFileHeader: [
|
||||
"// biome-ignore-all lint: generated by TanStack Router",
|
||||
],
|
||||
}),
|
||||
react(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": "/src",
|
||||
"@features-manifest": path.resolve(
|
||||
__dirname,
|
||||
"../preview-features.json",
|
||||
),
|
||||
"@protected-features": path.resolve(
|
||||
__dirname,
|
||||
protectedFeaturesEnabled
|
||||
? "./src/protectedFeatures/internal.ts"
|
||||
: "./src/protectedFeatures/public.ts",
|
||||
),
|
||||
"@model-capabilities-manifest": path.resolve(
|
||||
__dirname,
|
||||
"../scripts/model-capabilities.json",
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
//
|
||||
// 1. prevent Vite from obscuring rust errors
|
||||
clearScreen: false,
|
||||
// 2. tauri expects a fixed port, fail if that port is not available
|
||||
server: {
|
||||
port: parseInt(process.env.VITE_PORT || "1420", 10),
|
||||
strictPort: true,
|
||||
host: host || false,
|
||||
hmr: host
|
||||
? {
|
||||
protocol: "ws",
|
||||
host,
|
||||
port: parseInt(process.env.VITE_HMR_PORT || "1421", 10),
|
||||
}
|
||||
: undefined,
|
||||
watch: {
|
||||
// 3. tell Vite to ignore watching `src-tauri`
|
||||
ignored: ["**/src-tauri/**"],
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@@ -30,13 +30,6 @@
|
||||
"name": "Agent-managed profiles",
|
||||
"description": "Let agents manage their own relay name and avatar instead of restoring the desktop copy",
|
||||
"platforms": ["desktop"]
|
||||
},
|
||||
{
|
||||
"id": "bestie",
|
||||
"name": "Bestie",
|
||||
"description": "Try a personal agent that is always close at hand",
|
||||
"platforms": ["desktop"],
|
||||
"requiredBuildFlag": "bestie"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user