Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/cli-framework/src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ await run({
globals: config.globals,
help: config.help,
audiences: config.audiences?.(),
sandbox: config.sandbox?.(),
});
`,
);
Expand Down
2 changes: 2 additions & 0 deletions packages/cli-framework/src/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export type CommandConfig<
description: string;
aliases?: string[];
audience?: Audience;
/** false hides the command inside a cloud workspace, where it cannot work. */
sandbox?: false;
skipMiddleware?: TSkip;
options?: TOpts;
args?: TArgs;
Expand Down
2 changes: 2 additions & 0 deletions packages/cli-framework/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export interface CliConfig {
help?: HelpBranding;
/** Audiences whose commands exist for this run. Defaults to `["public"]`. */
audiences?: () => Audience[];
/** Whether this run is inside a cloud workspace; commands tagged `sandbox: false` are hidden there. */
sandbox?: () => boolean;
}

export function defineConfig(config: CliConfig): CliConfig {
Expand Down
1 change: 1 addition & 0 deletions packages/cli-framework/src/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,6 @@ export async function runDev(argv: string[]): Promise<void> {
globals: config.globals,
help: config.help,
audiences: config.audiences?.(),
sandbox: config.sandbox?.(),
});
}
31 changes: 30 additions & 1 deletion packages/cli-framework/src/router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ import { describe, expect, it } from "bun:test";
import type { CommandConfig } from "./command";
import { type CliCommand, type CliGroup, filterByAudience } from "./router";

function cmd(path: string[], audience?: CommandConfig["audience"]): CliCommand {
function cmd(
path: string[],
audience?: CommandConfig["audience"],
sandbox?: CommandConfig["sandbox"],
): CliCommand {
return {
path,
command: {
description: path.join(" "),
audience,
sandbox,
run: async () => undefined,
},
};
Expand Down Expand Up @@ -58,3 +63,27 @@ describe("filterByAudience", () => {
expect(result.groups).toEqual(groups);
});
});

describe("filterByAudience inside a cloud workspace", () => {
const groups: CliGroup[] = [
{ path: ["plugins"], description: "Plugins" },
{ path: ["hosts"], description: "Hosts", sandbox: false },
];
const commands = [
cmd(["plugins", "list"]),
cmd(["plugins", "install"], undefined, false),
cmd(["hosts", "list"]),
cmd(["auth", "login"], "public", false),
];

it("shows everything outside a cloud workspace", () => {
const outside = filterByAudience(groups, commands, ["public"]);
expect(paths(outside.commands)).toEqual(paths(commands));
});

it("hides sandbox: false commands and groups inside one", () => {
const inside = filterByAudience(groups, commands, ["public"], true);
expect(paths(inside.commands)).toEqual(["plugins list"]);
expect(paths(inside.groups)).toEqual(["plugins"]);
});
});
20 changes: 17 additions & 3 deletions packages/cli-framework/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type CliGroup = {
description: string;
aliases?: string[];
audience?: Audience;
sandbox?: false;
};

export type CliCommand = {
Expand All @@ -22,12 +23,25 @@ export function filterByAudience(
groups: CliGroup[],
commands: CliCommand[],
audiences: Audience[],
sandbox = false,
): { groups: CliGroup[]; commands: CliCommand[] } {
const hiddenPaths = [
...groups.map((g) => ({ path: g.path, audience: g.audience })),
...commands.map((c) => ({ path: c.path, audience: c.command.audience })),
...groups.map((g) => ({
path: g.path,
audience: g.audience,
sandbox: g.sandbox,
})),
...commands.map((c) => ({
path: c.path,
audience: c.command.audience,
sandbox: c.command.sandbox,
})),
]
.filter((node) => !audiences.includes(node.audience ?? "public"))
.filter(
(node) =>
!audiences.includes(node.audience ?? "public") ||
(sandbox && node.sandbox === false),
)
.map((node) => node.path);
const visibleCommands = commands.filter(
(c) => !hiddenPaths.some((hidden) => isPrefix(hidden, c.path)),
Expand Down
2 changes: 2 additions & 0 deletions packages/cli-framework/src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface RunOptions {
globals?: Record<string, GenericBuilderInternals>;
help?: HelpBranding;
audiences?: Audience[];
sandbox?: boolean;
}

export async function run(opts: RunOptions): Promise<void> {
Expand Down Expand Up @@ -247,6 +248,7 @@ async function execute(
loaded.groups,
loaded.commands,
opts.audiences ?? ["public"],
opts.sandbox ?? false,
);
const { root, commandMap } = buildTree(visible.groups, visible.commands);

Expand Down
1 change: 1 addition & 0 deletions packages/cli/cli.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default defineConfig({
process.env.SUPERSET_CLI_AUDIENCE === "internal"
? ["internal", "public"]
: ["public"],
sandbox: () => Boolean(process.env.SUPERSET_SANDBOX_WORKSPACE_ID),
help: {
tagline: "Command your fleet of coding agents from any shell.",
docsUrl: "https://docs.superset.sh/cli",
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands/auth/login/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ async function runApiKeyLogin({
}

export default command({
sandbox: false,
description: "Authenticate with Superset. Re-run to switch organizations.",
skipMiddleware: true,
options: {
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands/auth/logout/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { command } from "../../../lib/command";
import { readConfig, writeConfig } from "../../../lib/config";

export default command({
sandbox: false,
description: "Clear stored credentials",
skipMiddleware: true,
run: async () => {
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands/hosts/set-wake/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { resolveHost } from "../../../lib/host/resolve";
import { resolveOrganizationFromContext } from "../../../lib/resolve-org";

export default command({
sandbox: false,
description: "Set (or clear) the command used to wake a host",
args: [
positional("host").required().desc("Host name or id"),
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands/hosts/wake/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ function runLocally(cmd: string, signal: AbortSignal): Promise<number> {
}

export default command({
sandbox: false,
description: "Wake a host by running its configured wake command locally",
args: [positional("host").required().desc("Host name or id")],
options: {
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands/plugins/connect/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from "../../../lib/plugins/inputs";

export default command({
sandbox: false,
description: "Connect an account to an installed plugin",
args: [
positional("plugin")
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands/plugins/disable/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { resolvePluginRef } from "../../../lib/plugins/host";
import { setPluginEnabled } from "../../../lib/plugins/install";

export default command({
sandbox: false,
description: "Disable an installed plugin without dropping it",
args: [
positional("plugin")
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands/plugins/enable/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { resolvePluginRef } from "../../../lib/plugins/host";
import { setPluginEnabled } from "../../../lib/plugins/install";

export default command({
sandbox: false,
description: "Enable an installed plugin",
args: [
positional("plugin")
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands/plugins/install/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
import { pluginConnector } from "../../../lib/plugins/marketplace";

export default command({
sandbox: false,
description:
"Install a plugin: materialize its skills locally and record it on your account",
args: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
} from "../../../../lib/plugins/install";

export default command({
sandbox: false,
description: "Add a marketplace from a GitHub repo or local path",
args: [
positional("source")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { positional } from "@superset/cli-framework";
import { command } from "../../../../lib/command";

export default command({
sandbox: false,
description: "Remove a marketplace from your account",
args: [positional("name").required().desc("Marketplace name")],
run: async ({ ctx, args }) => {
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands/plugins/uninstall/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { resolvePluginRef } from "../../../lib/plugins/host";
import { removePlugin } from "../../../lib/plugins/install";

export default command({
sandbox: false,
description: "Uninstall a plugin and drop its skills",
aliases: ["remove"],
args: [
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands/start/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
import { resolveOrganization } from "../../lib/resolve-org";

export default command({
sandbox: false,
description: "Start the host service",
options: {
daemon: boolean().desc("Run in background"),
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands/stop/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from "../../lib/host/manifest";

export default command({
sandbox: false,
description: "Stop the host service daemon",
run: async ({ ctx }) => {
const organization = await ctx.api.user.myOrganization.query();
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands/update/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ function resolveInstallRoot(): string {
}

export default command({
sandbox: false,
description: "Update the Superset CLI and host service to the latest release",
skipMiddleware: true,
options: {
Expand Down
39 changes: 0 additions & 39 deletions packages/workspace-fs/src/watch-backend/watch-backend.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { afterEach, describe, expect, test } from "bun:test";
import { mkdir, mkdtemp, realpath, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { FsWatcherManager } from "../watch";
import {
chokidarWatchBackend,
createIgnoreMatcher,
Expand Down Expand Up @@ -145,44 +144,6 @@ for (const backend of [parcelWatchBackend, chokidarWatchBackend]) {
});
}

describe("FsWatcherManager on the chokidar backend", () => {
test("emits events, prunes ignored dirs, and recovers a recreated root", async () => {
const parent = await makeRoot();
const root = path.join(parent, "worktree");
await mkdir(path.join(root, "node_modules"), { recursive: true });
const manager = new FsWatcherManager({
backend: chokidarWatchBackend,
debounceMs: 20,
recoveryPollMs: 100,
});
cleanups.push(() => manager.close());
const seen: Array<{ kind: string; absolutePath: string }> = [];
await manager.subscribe({ absolutePath: root }, (batch) => {
seen.push(...batch.events);
});
const saw = (kind: string, target: string) =>
seen.some((e) => e.kind === kind && e.absolutePath === target);

await writeFile(path.join(root, "node_modules", "ignored.js"), "x");
const file = path.join(root, "src.ts");
await writeFile(file, "x");
await waitFor(() => saw("create", file), "manager create");
expect(seen.some((e) => e.absolutePath.includes("node_modules"))).toBe(
false,
);
expect(manager.isPathPruned(root, file)).toBe(false);

await rm(root, { recursive: true, force: true });
await waitFor(() => saw("delete", root), "root delete");
seen.length = 0;
await mkdir(root, { recursive: true });
await waitFor(() => saw("create", root), "root recreated");
const after = path.join(root, "after.ts");
await writeFile(after, "x");
await waitFor(() => saw("create", after), "event after recovery");
}, 30_000);
});

describe("createIgnoreMatcher", () => {
const isIgnored = createIgnoreMatcher("/repo", [
"**/node_modules/**",
Expand Down
Loading