@dbx-tools/cli 0.1.111 → 0.3.1

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/src/root.ts ADDED
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Workspace root detection for the `dbxtools` CLI.
3
+ */
4
+ import { existsSync } from "node:fs";
5
+ import { basename, dirname, join, resolve } from "node:path";
6
+ import { exec } from "@dbx-tools/core";
7
+ import { functionModule } from "@dbx-tools/shared-core";
8
+
9
+ async function gitToplevel(): Promise<string | undefined> {
10
+ const { exitCode, stdout } = await exec.spawn("git", ["rev-parse", "--show-toplevel"], {
11
+ stdout: "capture",
12
+ stderr: "ignore",
13
+ stdin: "ignore",
14
+ });
15
+ if (exitCode !== 0) return undefined;
16
+ return stdout || undefined;
17
+ }
18
+
19
+ /**
20
+ * Walk upward from `startDir` for `.projenrc.ts`. If none is found, try git
21
+ * top-level only when that directory also contains `.projenrc.ts`; otherwise return
22
+ * `resolve(startDir)` (which may not be a workspace root).
23
+ */
24
+ export async function findWorkspaceRoot(startDir: string = process.cwd()): Promise<string> {
25
+ let dir = resolve(startDir);
26
+ while (true) {
27
+ if (existsSync(join(dir, ".projenrc.ts"))) return dir;
28
+ const parent = dirname(dir);
29
+ if (parent === dir) break;
30
+ dir = parent;
31
+ }
32
+
33
+ const fromGit = await gitToplevel();
34
+ if (fromGit && existsSync(join(fromGit, ".projenrc.ts"))) return fromGit;
35
+
36
+ return resolve(startDir);
37
+ }
38
+
39
+ /**
40
+ * True when the folder has no `.projenrc.ts` yet and needs full bootstrapping.
41
+ * Keying off `.projenrc.ts` (not `package.json`) avoids clobbering a cleaned
42
+ * workspace that still has a hand-authored projenrc.
43
+ */
44
+ export function needsBootstrap(root: string): boolean {
45
+ return !existsSync(join(root, ".projenrc.ts"));
46
+ }
47
+
48
+ /** True when `node_modules` or projen itself is missing under `root`. */
49
+ export function needsInstall(root: string): boolean {
50
+ if (!existsSync(join(root, "node_modules"))) return true;
51
+ if (existsSync(join(root, ".projenrc.ts")) && !existsSync(join(root, "node_modules", "projen"))) {
52
+ return true;
53
+ }
54
+ return false;
55
+ }
56
+
57
+ /**
58
+ * True when the synth TOOLCHAIN isn't installed yet: no `node_modules`, or the
59
+ * dbx-tools engine / `projen` aren't resolvable under it. Distinct from a full
60
+ * bootstrap (which keys off a MISSING `.projenrc.ts`): here the projenrc exists
61
+ * but its dependencies don't - e.g. a copied project whose generated manifests
62
+ * and `node_modules` are gitignored. Seeding the toolchain (not scaffolding)
63
+ * makes it synth-ready without touching the hand-authored `.projenrc.ts`.
64
+ */
65
+ export function needsToolchain(root: string): boolean {
66
+ const modules = join(root, "node_modules");
67
+ if (!existsSync(modules)) return true;
68
+ return (
69
+ !existsSync(join(modules, "projen")) ||
70
+ !existsSync(join(modules, "@dbx-tools", "projen"))
71
+ );
72
+ }
73
+
74
+ /** Async, memoized root lookup from the process cwd at first use. */
75
+ export const workspaceRoot = functionModule.memoize(() => findWorkspaceRoot());
76
+
77
+ /** Short label for log output (`basename` of the resolved root). */
78
+ export function rootLabel(root: string): string {
79
+ return basename(root) || root;
80
+ }
@@ -0,0 +1,14 @@
1
+ // ~~ Generated by projen. To modify, edit .projenrc.js and run "pnpm exec projen".
2
+ {
3
+ "extends": "../tsconfig.json",
4
+ "compilerOptions": {
5
+ "noEmit": true,
6
+ "rootDir": ".."
7
+ },
8
+ "include": [
9
+ "**/*.ts"
10
+ ],
11
+ "exclude": [
12
+ "node_modules"
13
+ ]
14
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,43 @@
1
+ // ~~ Generated by projen. To modify, edit .projenrc.js and run "pnpm exec projen".
2
+ {
3
+ "compilerOptions": {
4
+ "rootDir": ".",
5
+ "outDir": "lib",
6
+ "alwaysStrict": true,
7
+ "declaration": true,
8
+ "esModuleInterop": true,
9
+ "experimentalDecorators": true,
10
+ "inlineSourceMap": true,
11
+ "inlineSources": true,
12
+ "lib": [
13
+ "ES2022"
14
+ ],
15
+ "module": "ESNext",
16
+ "noEmitOnError": false,
17
+ "noFallthroughCasesInSwitch": true,
18
+ "noImplicitAny": true,
19
+ "noImplicitReturns": true,
20
+ "noImplicitThis": true,
21
+ "noUnusedLocals": true,
22
+ "noUnusedParameters": true,
23
+ "resolveJsonModule": true,
24
+ "strict": true,
25
+ "strictNullChecks": true,
26
+ "strictPropertyInitialization": true,
27
+ "stripInternal": true,
28
+ "target": "ES2022",
29
+ "types": [
30
+ "node"
31
+ ],
32
+ "moduleResolution": "bundler",
33
+ "skipLibCheck": true
34
+ },
35
+ "include": [
36
+ "src/**/*.ts",
37
+ "index.ts",
38
+ "bin/**/*.ts"
39
+ ],
40
+ "exclude": [
41
+ "node_modules"
42
+ ]
43
+ }
@@ -1 +0,0 @@
1
- export { };
@@ -1,55 +0,0 @@
1
- #!/usr/bin/env bun
2
- import { C as codegen, S as build, b as resolveAgentPrompt, d as release, f as format, g as agent, l as notesSinceRequested, n as forwardedUpdateArgs, p as create, s as update, t as verify, u as tag } from "../verify-ZnM5Gb2r.js";
3
- import { Command, InvalidArgumentError } from "commander";
4
-
5
- //#region packages/cli/bin/dbxtools.ts
6
- const program = new Command().name("dbxtools").description("Workspace build, scaffold, and release toolkit for Bun monorepos.");
7
- program.command("format").description("syncpack format, regroup lifecycle hooks, then prettier --write.").action(format);
8
- program.command("build").description("Compile every publishable package with the shared tsdown config.").action(build);
9
- program.command("release").description("Build, then publish each package with a stamped (complete) manifest.").option("-n, --dry-run", "rehearse with `bun publish --dry-run`", false).action(async (opts) => {
10
- await release({ dryRun: opts.dryRun });
11
- });
12
- program.command("codegen").description("Regenerate each package's `generated/` zod tree from its inputs.").action(codegen);
13
- program.command("verify").description("Workspace verify pass (optional sibling dependency scan).").option("--workspace-deps", "fail on imports of sibling packages not declared as dependencies", false).action(async (opts) => {
14
- await verify({ workspaceDeps: opts.workspaceDeps });
15
- });
16
- program.command("update").description("Pin root catalog entries to latest stable versions, then bun update at the repo root.").allowUnknownOption().action(async () => {
17
- await update(forwardedUpdateArgs());
18
- });
19
- program.command("create").description("Scaffold a new workspace package under packages/<slug>/.").argument("<slug>", "kebab-case slug (lowercase, starts with a letter)", (value) => {
20
- if (!/^[a-z][a-z0-9-]*$/.test(value)) throw new InvalidArgumentError(`invalid slug "${value}"`);
21
- return value;
22
- }).option("--plugin", "scaffold an AppKit plugin package").option("--shared", "scaffold a browser-safe shared package").action(async (slug, opts) => {
23
- await create({
24
- slug,
25
- plugin: opts.plugin,
26
- shared: opts.shared
27
- });
28
- });
29
- program.command("agent").description("Run ucode codex exec with assistant output.").argument("[prompt...]", "prompt text (or pipe via stdin when omitted)").option("-t, --timeout <seconds>", "wall-clock budget in seconds (default 300)", (value) => {
30
- const seconds = Number(value);
31
- if (!Number.isFinite(seconds) || seconds <= 0) throw new InvalidArgumentError("timeout must be a positive number");
32
- return Math.round(seconds * 1e3);
33
- }).action(async (promptParts, opts) => {
34
- const code = await agent({
35
- prompt: await resolveAgentPrompt(promptParts),
36
- timeoutMs: opts.timeout
37
- });
38
- if (code !== 0) process.exit(code);
39
- });
40
- program.command("tag").description("Bump every workspace, commit, tag HEAD, and push to origin.").argument("[bump]", "version bump (patch | minor | major)", (value) => {
41
- if (value !== "patch" && value !== "minor" && value !== "major") throw new InvalidArgumentError("expected patch, minor, or major");
42
- return value;
43
- }, "patch").option("--no-publish", "skip publishing the tagged versions to the local registry").option("-n, --dry-run", "print the plan and release-notes preview; write nothing, push nothing", false).option("--notes-since <tag>", "widen release-notes baseline to this tag (e.g. v0.1.75); default is the previous tag on HEAD").option("--no-ai-notes", "skip ucode codex; use commit-grouped notes only").action(async (bump, opts) => {
44
- await tag({
45
- bump,
46
- dryRun: opts.dryRun,
47
- publish: opts.publish,
48
- aiNotes: opts.aiNotes,
49
- ...notesSinceRequested(opts.notesSince) ? { notesSince: opts.notesSince } : {}
50
- });
51
- });
52
- await program.parseAsync(process.argv);
53
-
54
- //#endregion
55
- export { };
package/dist/index.d.ts DELETED
@@ -1,289 +0,0 @@
1
- import { FileSystemProject, Workspace } from "pacwich";
2
-
3
- //#region packages/cli/src/build.d.ts
4
- /** Compile every publishable package with the shared tsdown config. */
5
- declare function build(): Promise<void>;
6
- //#endregion
7
- //#region packages/cli/src/exec.d.ts
8
- /**
9
- * Subprocess helper with optional per-line stdout/stderr callbacks.
10
- *
11
- * Defaults to `Bun.spawn` so line callbacks stream as output arrives. Set
12
- * `shell: true` to use Bun's cross-platform `$` shell (PATH resolution,
13
- * Windows `.cmd` handling) when you do not need streaming line handlers.
14
- *
15
- * For capture-and-throw semantics, prefer {@link sh} / {@link bunx}.
16
- */
17
- type BunSpawnOptions = NonNullable<Parameters<typeof Bun.spawn>[1]>;
18
- type ExecOptions = Omit<BunSpawnOptions, "stdin" | "stdout" | "stderr" | "stdio"> & {
19
- stdin?: BunSpawnOptions["stdin"];
20
- stdout?: BunSpawnOptions["stdout"] | ((line: string) => void);
21
- stderr?: BunSpawnOptions["stderr"] | ((line: string) => void);
22
- /**
23
- * Run through Bun's `$` shell instead of `Bun.spawn`. Cross-platform
24
- * command resolution; stdout/stderr echo to the terminal by default.
25
- * Line callbacks are satisfied from buffered output after exit (Bun
26
- * shell does not stream `.lines()` while the process runs).
27
- */
28
- shell?: boolean; /** Shell only: suppress live stdout/stderr echo (output is still buffered). */
29
- quiet?: boolean;
30
- };
31
- /**
32
- * Spawn a subprocess and wait for exit.
33
- *
34
- * Unset stdio fds default to `"inherit"`. Pass a function as `stdout` or
35
- * `stderr` to receive each line; with the default `Bun.spawn` path that fd
36
- * is piped and lines stream as they arrive.
37
- */
38
- declare function exec(command: string, args: string[], options?: ExecOptions): Promise<number>;
39
- //#endregion
40
- //#region packages/cli/src/agent.d.ts
41
- /**
42
- * Run Codex headlessly via `ucode codex exec` for `dbxtools agent` and
43
- * release tooling that drafts notes programmatically.
44
- */
45
- /** Default wall-clock budget for a Codex invocation. */
46
- declare const AGENT_DEFAULT_TIMEOUT_MS = 300000;
47
- /** Options for {@link runAgent}. */
48
- interface AgentOptions {
49
- /** Wall-clock budget in milliseconds. */
50
- timeoutMs?: number;
51
- /** Working directory for the agent (defaults to `process.cwd()`). */
52
- cwd?: string;
53
- /**
54
- * When `true` (default), capture stdout via `sh({ quiet: true })` for
55
- * programmatic callers. When `false`, inherit stdio like
56
- * `ucode codex exec …` in a shell.
57
- */
58
- capture?: boolean;
59
- /**
60
- * When `true` (default) and `capture` is enabled, write the parsed
61
- * assistant text to stdout. Set `false` when the caller will log the
62
- * result itself (e.g. release-notes drafting).
63
- */
64
- echo?: boolean;
65
- }
66
- /** Outcome of {@link runAgent}. */
67
- interface AgentResult {
68
- /** Trimmed assistant answer. */
69
- text: string;
70
- /** Subprocess exit code. */
71
- exitCode: number;
72
- /** Trimmed stderr. */
73
- stderr: string;
74
- }
75
- /** Options for {@link agent}. */
76
- interface AgentCommandOptions {
77
- /** Prompt passed to `ucode codex exec`. */
78
- prompt: string;
79
- /** Wall-clock budget in milliseconds. */
80
- timeoutMs?: number;
81
- }
82
- /** Whether `ucode codex --version` reports a usable Codex CLI. */
83
- declare function agentAvailable(): Promise<boolean>;
84
- /** Whether an exit code looks like the process was killed on a timeout. */
85
- declare function agentTimedOut(exitCode: number): boolean;
86
- /** Pull assistant prose out of `ucode codex exec` stdout. */
87
- declare function parseCodexStdout(stdout: string): string;
88
- /**
89
- * Run `ucode codex exec` and return captured output. Throws when Codex
90
- * is absent or the wall-clock budget is exceeded. On a non-zero exit
91
- * the result is still returned so callers can use partial text.
92
- */
93
- declare function runAgent(prompt: string, opts?: AgentOptions): Promise<AgentResult>;
94
- /**
95
- * `dbxtools agent` entry: run Codex and return a process exit code (0 on
96
- * success, 1 on failure or timeout).
97
- */
98
- declare function agent(opts: AgentCommandOptions): Promise<number>;
99
- /** Join argv prompt parts, or read stdin when non-interactive and args are empty. */
100
- declare function resolveAgentPrompt(promptParts: string[]): Promise<string>;
101
- //#endregion
102
- //#region packages/cli/src/codegen.d.ts
103
- /** Regenerate the `generated/` tree for every package declaring a `codegen` field. */
104
- declare function codegen(): Promise<void>;
105
- //#endregion
106
- //#region packages/cli/src/config.d.ts
107
- /** Resolved toolkit configuration for the current repo. */
108
- interface DbxtoolsConfig {
109
- /** npm scope (e.g. `@acme`) used when scaffolding new packages. */
110
- scope: string;
111
- /** `owner/name` slug used for release links, or null when undiscoverable. */
112
- repo: string | null;
113
- /**
114
- * Name of the shared-helpers package `create` wires as a dependency
115
- * into new plugin / standard packages (when it exists in the
116
- * workspace). Defaults to `<scope>/shared`, or null when no scope
117
- * resolves.
118
- */
119
- sharedPackage: string | null;
120
- }
121
- /**
122
- * Resolve toolkit config for the current repo, memoized for the
123
- * process. Overrides under the root `package.json` `dbxtools` key win
124
- * over the auto-derived defaults.
125
- */
126
- declare const getDbxtoolsConfig: () => Promise<DbxtoolsConfig>;
127
- //#endregion
128
- //#region packages/cli/src/create.d.ts
129
- /** Options for {@link create}. */
130
- interface CreateOptions {
131
- /** kebab-case slug (lowercase, starts with a letter). */
132
- slug: string;
133
- /** Scaffold an AppKit plugin package. */
134
- plugin?: boolean;
135
- /** Scaffold a browser-safe shared package. */
136
- shared?: boolean;
137
- }
138
- /** Scaffold a new workspace package under `packages/<slug>/`. */
139
- declare function create(options: CreateOptions): Promise<void>;
140
- //#endregion
141
- //#region packages/cli/src/format.d.ts
142
- /** syncpack + lifecycle-hook regroup + prettier across the workspace. */
143
- declare function format(): Promise<void>;
144
- //#endregion
145
- //#region packages/cli/src/shell.d.ts
146
- interface ShellResult {
147
- exitCode: number;
148
- stdout: string;
149
- stderr: string;
150
- }
151
- interface ShellOptions {
152
- /** Return the result on non-zero exit instead of throwing. */
153
- nothrow?: boolean;
154
- /** Working directory for the command. */
155
- cwd?: string;
156
- /** String piped to the command's stdin. */
157
- input?: string;
158
- /** Suppress the live echo. Output is still captured on the result. */
159
- quiet?: boolean;
160
- }
161
- /**
162
- * Run a command, streaming live unless `quiet`. Returns trimmed captured
163
- * output regardless. Throws on non-zero unless `nothrow`.
164
- */
165
- declare function sh(args: string[], opts?: ShellOptions): Promise<ShellResult>;
166
- /** `bun x <args>` for one-off CLI tools (knip, syncpack, prettier, ...). */
167
- declare function bunx(args: string[], opts?: ShellOptions): Promise<ShellResult>;
168
- //#endregion
169
- //#region packages/cli/src/git.d.ts
170
- /**
171
- * Run `git <args>`, returning trimmed output. Quiet by default since git is
172
- * used mostly for its stdout (rev-parse, log, diff, ...); pass `quiet: false`
173
- * to stream a mutating op live. Throws on non-zero unless `nothrow`.
174
- */
175
- declare function git(args: string[], opts?: {
176
- nothrow?: boolean;
177
- quiet?: boolean;
178
- cwd?: string;
179
- }): Promise<ShellResult>;
180
- //#endregion
181
- //#region packages/cli/src/package.d.ts
182
- /** Minimal package.json shape the commands care about. */
183
- interface PackageJson {
184
- name?: string;
185
- version?: string;
186
- private?: boolean;
187
- workspaces?: string[];
188
- [key: string]: unknown;
189
- }
190
- /** A workspace package: its parsed manifest, location, and dependency edges. */
191
- declare class WorkspacePackage {
192
- readonly meta: PackageJson;
193
- readonly dir: string;
194
- readonly slug: string;
195
- readonly jsonPath: string;
196
- private constructor();
197
- /** Build from a pacwich {@link Workspace}, reading its manifest. */
198
- static fromWorkspace(ws: Workspace): Promise<WorkspacePackage>;
199
- }
200
- /** Resolve `path` against the repo root. */
201
- declare function toAbsolute(path: string): string;
202
- /** Repo-relative form of `path`, or absolute when it sits outside the root. */
203
- declare function toRelative(path: string): string;
204
- /** Yield every workspace `package.json` path (`includeRoot` prepends the root manifest). */
205
- declare function discoverPackageJsons(includeRoot?: boolean): AsyncIterableIterator<string>;
206
- /** Workspace packages passing `filter` (default: non-private), sorted by slug. */
207
- declare function discoverPackages(filter?: (pkg: WorkspacePackage) => boolean): Promise<WorkspacePackage[]>;
208
- /** Write `value` as JSON, preserving the file's trailing newline to avoid format churn. */
209
- declare function writeJson(path: string, value: unknown): Promise<void>;
210
- //#endregion
211
- //#region packages/cli/src/project.d.ts
212
- declare const getProject: () => Promise<FileSystemProject>;
213
- //#endregion
214
- //#region packages/cli/src/release.d.ts
215
- /** Options for {@link release}. */
216
- interface ReleaseOptions {
217
- /** Rehearse with `bun publish --dry-run` instead of publishing. */
218
- dryRun?: boolean;
219
- }
220
- /**
221
- * Build every publishable package, then publish each with a stamped
222
- * (complete) `package.json`, restoring the slim source manifest after
223
- * each publish whether it succeeds or fails.
224
- */
225
- declare function release(opts?: ReleaseOptions): Promise<void>;
226
- //#endregion
227
- //#region packages/cli/src/script.d.ts
228
- /** Log `message` and exit non-zero. */
229
- declare function fail(message: string): never;
230
- /** Narrow an unknown thrown value to its message string. */
231
- declare function errorMessage(err: unknown): string;
232
- /** Split text on newlines, trimming each line and dropping blanks. */
233
- declare function nonEmptyLines(text: string): string[];
234
- //#endregion
235
- //#region packages/cli/src/tag.d.ts
236
- type Bump = "major" | "minor" | "patch";
237
- /** Options for {@link tag}. */
238
- interface TagOptions {
239
- /** Version bump (defaults to `patch`). */
240
- bump?: Bump;
241
- /** Print everything, write nothing. */
242
- dryRun?: boolean;
243
- /** Publish the tagged versions to the local registry (default true). */
244
- publish?: boolean;
245
- /**
246
- * When set, widen the release-notes baseline to this tag instead of
247
- * the latest tag on `HEAD` (e.g. `v0.1.75` when several recent tags
248
- * failed to publish). Omit to use the previous tag only.
249
- */
250
- notesSince?: string;
251
- /** When false, skip Codex and use commit-grouped notes only. */
252
- aiNotes?: boolean;
253
- }
254
- /**
255
- * Version-bump every publishable workspace, commit, tag, push, create
256
- * the GitHub Release (with generated notes), and publish to the local
257
- * registry. See the file header for the full local-state policy.
258
- */
259
- declare function tag(opts?: TagOptions): Promise<void>;
260
- //#endregion
261
- //#region packages/cli/src/update.d.ts
262
- /** True when `version` is a release with no prerelease segment. */
263
- declare function isStableVersion(version: string): boolean;
264
- /** Highest stable version in `versions` that satisfies `range`. */
265
- declare function latestStableInRange(versions: string[], range: string): string | null;
266
- /** Rewrite a single range (or `latest`) to a caret pin on the latest stable match. */
267
- declare function stableCaretRange(versions: string[], range: string): string;
268
- /** Refresh every root `catalog` entry to the latest stable release in-range. */
269
- declare function updateCatalog(): Promise<boolean>;
270
- /** Run `bun update` with `forwardArgs` at the repo root. */
271
- declare function runBunUpdate(forwardArgs: string[]): Promise<void>;
272
- /** Refresh catalog pins, then `bun update` at the repo root. */
273
- declare function update(forwardArgs?: string[]): Promise<void>;
274
- /** Args after the `update` subcommand in `process.argv`. */
275
- declare function forwardedUpdateArgs(argv?: string[]): string[];
276
- //#endregion
277
- //#region packages/cli/src/verify.d.ts
278
- /** Options for {@link verify}. */
279
- interface VerifyOptions {
280
- /**
281
- * When true, fail on imports of sibling workspace packages not declared
282
- * as dependencies. Off by default.
283
- */
284
- workspaceDeps?: boolean;
285
- }
286
- /** Workspace verify pass (optional implicit-dependency scan). */
287
- declare function verify(options?: VerifyOptions): Promise<void>;
288
- //#endregion
289
- export { AGENT_DEFAULT_TIMEOUT_MS, type AgentCommandOptions, type AgentOptions, type AgentResult, type Bump, type CreateOptions, type DbxtoolsConfig, type ExecOptions, type PackageJson, type ReleaseOptions, type ShellResult, type TagOptions, type VerifyOptions, WorkspacePackage, agent, agentAvailable, agentTimedOut, build, bunx, codegen, create, discoverPackageJsons, discoverPackages, errorMessage, exec, fail, format, forwardedUpdateArgs, getDbxtoolsConfig, getProject, git, isStableVersion, latestStableInRange, nonEmptyLines, parseCodexStdout, release, resolveAgentPrompt, runAgent, runBunUpdate, sh, stableCaretRange, tag, toAbsolute, toRelative, update, updateCatalog, verify, writeJson };
package/dist/index.js DELETED
@@ -1,103 +0,0 @@
1
- import { A as git, C as codegen, D as toAbsolute, E as discoverPackages, F as nonEmptyLines, I as getProject, M as sh, N as errorMessage, O as toRelative, P as fail, S as build, T as discoverPackageJsons, _ as agentAvailable, a as runBunUpdate, b as resolveAgentPrompt, c as updateCatalog, d as release, f as format, g as agent, h as AGENT_DEFAULT_TIMEOUT_MS, i as latestStableInRange, j as bunx, k as writeJson, m as getDbxtoolsConfig, n as forwardedUpdateArgs, o as stableCaretRange, p as create, r as isStableVersion, s as update, t as verify, u as tag, v as agentTimedOut, w as WorkspacePackage, x as runAgent, y as parseCodexStdout } from "./verify-ZnM5Gb2r.js";
2
- import { $ } from "bun";
3
- import * as readline from "node:readline";
4
- import { Readable } from "node:stream";
5
-
6
- //#region packages/cli/src/exec.ts
7
- /**
8
- * Subprocess helper with optional per-line stdout/stderr callbacks.
9
- *
10
- * Defaults to `Bun.spawn` so line callbacks stream as output arrives. Set
11
- * `shell: true` to use Bun's cross-platform `$` shell (PATH resolution,
12
- * Windows `.cmd` handling) when you do not need streaming line handlers.
13
- *
14
- * For capture-and-throw semantics, prefer {@link sh} / {@link bunx}.
15
- */
16
- /**
17
- * Spawn a subprocess and wait for exit.
18
- *
19
- * Unset stdio fds default to `"inherit"`. Pass a function as `stdout` or
20
- * `stderr` to receive each line; with the default `Bun.spawn` path that fd
21
- * is piped and lines stream as they arrive.
22
- */
23
- async function exec(command, args, options = {}) {
24
- if (options.shell) return execShell(command, args, options);
25
- return execSpawn(command, args, options);
26
- }
27
- async function execSpawn(command, args, options) {
28
- const { stdin, stdout, stderr, shell: _shell, quiet: _quiet, ...spawnOpts } = options;
29
- const onStdout = typeof stdout === "function" ? stdout : void 0;
30
- const onStderr = typeof stderr === "function" ? stderr : void 0;
31
- const stdoutMode = typeof stdout === "function" ? "pipe" : stdout ?? "inherit";
32
- const stderrMode = typeof stderr === "function" ? "pipe" : stderr ?? "inherit";
33
- const proc = Bun.spawn([command, ...args], {
34
- ...spawnOpts,
35
- stdin: stdin ?? "inherit",
36
- stdout: stdoutMode,
37
- stderr: stderrMode
38
- });
39
- const reads = [];
40
- if (onStdout && isReadableStream(proc.stdout)) reads.push(readLines(proc.stdout, onStdout));
41
- if (onStderr && isReadableStream(proc.stderr)) reads.push(readLines(proc.stderr, onStderr));
42
- try {
43
- const exitCode = await proc.exited;
44
- await Promise.all(reads);
45
- return exitCode;
46
- } catch (err) {
47
- await Promise.allSettled(reads);
48
- throw err;
49
- }
50
- }
51
- async function execShell(command, args, options) {
52
- const { cwd, env, quiet, stdout, stderr, stdin, shell: _shell, ..._spawnOnly } = options;
53
- const onStdout = typeof stdout === "function" ? stdout : void 0;
54
- const onStderr = typeof stderr === "function" ? stderr : void 0;
55
- const argv = [command, ...args];
56
- let cmd = typeof stdin === "string" ? $`${argv} < ${new Response(stdin)}`.nothrow() : $`${argv}`.nothrow();
57
- if (cwd) cmd = cmd.cwd(cwd);
58
- if (env) cmd = cmd.env(env);
59
- if (quiet) cmd = cmd.quiet();
60
- if (onStdout) {
61
- const [res] = await Promise.all([cmd, (async () => {
62
- for await (const line of cmd.lines()) onStdout(line);
63
- })()]);
64
- if (onStderr) emitBufferedLines(res.stderr.toString(), onStderr);
65
- return res.exitCode;
66
- }
67
- const res = await cmd;
68
- if (onStderr) emitBufferedLines(res.stderr.toString(), onStderr);
69
- return res.exitCode;
70
- }
71
- function isReadableStream(value) {
72
- return typeof value === "object" && value !== null && "getReader" in value;
73
- }
74
- async function readLines(stream, onLine) {
75
- const rl = readline.createInterface({
76
- input: Readable.fromWeb(stream),
77
- crlfDelay: Infinity
78
- });
79
- for await (const line of rl) onLine(line);
80
- }
81
- function emitBufferedLines(text, onLine) {
82
- if (text.length === 0) return;
83
- const body = text.endsWith("\n") ? text.slice(0, -1) : text;
84
- for (const line of body.split("\n")) onLine(line.endsWith("\r") ? line.slice(0, -1) : line);
85
- }
86
- if (import.meta.main) {
87
- const useShell = process.argv.includes("--shell");
88
- const [command, ...args] = process.argv.slice(2).filter((arg) => arg !== "--shell");
89
- if (!command) {
90
- console.error("Usage: exec [--shell] <command> [args...]");
91
- process.exit(1);
92
- }
93
- const exitCode = await exec(command, args, {
94
- shell: useShell,
95
- stdout: (line) => console.log(line),
96
- stderr: (line) => console.error(line)
97
- });
98
- console.log(`Exit code: ${exitCode}`);
99
- process.exit(exitCode);
100
- }
101
-
102
- //#endregion
103
- export { AGENT_DEFAULT_TIMEOUT_MS, WorkspacePackage, agent, agentAvailable, agentTimedOut, build, bunx, codegen, create, discoverPackageJsons, discoverPackages, errorMessage, exec, fail, format, forwardedUpdateArgs, getDbxtoolsConfig, getProject, git, isStableVersion, latestStableInRange, nonEmptyLines, parseCodexStdout, release, resolveAgentPrompt, runAgent, runBunUpdate, sh, stableCaretRange, tag, toAbsolute, toRelative, update, updateCatalog, verify, writeJson };