@wolfstar/cli 0.2.0 → 0.4.0
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/README.md +53 -27
- package/dist/{build-B6xw00Ee.js → build-B1YfbqX3.js} +2 -2
- package/dist/{build-B6xw00Ee.js.map → build-B1YfbqX3.js.map} +1 -1
- package/dist/{build-CvSvbsYN.js → build-vpcF5bwc.js} +5 -4
- package/dist/build-vpcF5bwc.js.map +1 -0
- package/dist/cli.js +6 -6
- package/dist/{codegen-mYkL-GBf.js → codegen-1kW_OnrL.js} +2 -2
- package/dist/{codegen-mYkL-GBf.js.map → codegen-1kW_OnrL.js.map} +1 -1
- package/dist/{codegen-DjPOn7cp.js → codegen-DHQH83M3.js} +2 -2
- package/dist/{codegen-DjPOn7cp.js.map → codegen-DHQH83M3.js.map} +1 -1
- package/dist/{commands-Dw2LFWV-.js → commands-DPGDL7ft.js} +4 -4
- package/dist/commands-DPGDL7ft.js.map +1 -0
- package/dist/{commands-BkGKs_Bg.js → commands-hpnmaBM2.js} +4 -4
- package/dist/{commands-BkGKs_Bg.js.map → commands-hpnmaBM2.js.map} +1 -1
- package/dist/{dev-CWA_JVTb.js → dev-Dh1Euxk1.js} +111 -68
- package/dist/dev-Dh1Euxk1.js.map +1 -0
- package/dist/{dev-BCgaVd0m.js → dev-hT1WR1Kh.js} +2 -2
- package/dist/{dev-BCgaVd0m.js.map → dev-hT1WR1Kh.js.map} +1 -1
- package/dist/external-Bk4EESdT.js.map +1 -1
- package/dist/index.d.ts +14 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{info-Czh4LUsD.js → info-BrxKCcUW.js} +2 -2
- package/dist/{info-Czh4LUsD.js.map → info-BrxKCcUW.js.map} +1 -1
- package/dist/{info-BqxpktLG.js → info-rYRLITnv.js} +2 -2
- package/dist/{info-BqxpktLG.js.map → info-rYRLITnv.js.map} +1 -1
- package/dist/locales-B3mRenhY.js +104 -0
- package/dist/locales-B3mRenhY.js.map +1 -0
- package/dist/log-buffer-D-nj2ZEU.js +72 -0
- package/dist/log-buffer-D-nj2ZEU.js.map +1 -0
- package/dist/none-DEo7zEr9.js.map +1 -1
- package/dist/{output-mode-ChrOf0LO.js → output-mode-Cxes6YUW.js} +4 -2
- package/dist/output-mode-Cxes6YUW.js.map +1 -0
- package/dist/{prepare-BXcm2Ukp.js → prepare-90XQCoak.js} +1 -1
- package/dist/{prepare-D5NZ0w4t.js → prepare-CHLrneYU.js} +2 -2
- package/dist/{prepare-D5NZ0w4t.js.map → prepare-CHLrneYU.js.map} +1 -1
- package/dist/{prepare-CVf88_Or.js → prepare-hOwjyelr.js} +2 -2
- package/dist/{prepare-CVf88_Or.js.map → prepare-hOwjyelr.js.map} +1 -1
- package/dist/process-supervisor-CJOdmp0W.js.map +1 -1
- package/dist/tsc-BqZfDTD0.js.map +1 -1
- package/dist/{tsdown-BEFq2API.js → tsdown-DN3PQTJf.js} +30 -13
- package/dist/tsdown-DN3PQTJf.js.map +1 -0
- package/dist/tui-BCGQrMQo.js +947 -0
- package/dist/tui-BCGQrMQo.js.map +1 -0
- package/dist/{tunnel-B8Id4-lR.js → tunnel-mGJe2zuE.js} +20 -7
- package/dist/tunnel-mGJe2zuE.js.map +1 -0
- package/dist/vite-27ZbreDz.js.map +1 -1
- package/package.json +10 -9
- package/dist/build-CvSvbsYN.js.map +0 -1
- package/dist/builders-D97Uqjwg.js +0 -44
- package/dist/builders-D97Uqjwg.js.map +0 -1
- package/dist/commands-Dw2LFWV-.js.map +0 -1
- package/dist/dev-CWA_JVTb.js.map +0 -1
- package/dist/output-mode-ChrOf0LO.js.map +0 -1
- package/dist/tsdown-BEFq2API.js.map +0 -1
- package/dist/tui-BTRFsLQR.js +0 -770
- package/dist/tui-BTRFsLQR.js.map +0 -1
- package/dist/tunnel-B8Id4-lR.js.map +0 -1
package/README.md
CHANGED
|
@@ -41,13 +41,10 @@ Projects scaffolded with [`@wolfstar/create-http-framework`](../create-http-fram
|
|
|
41
41
|
// stars.config.ts
|
|
42
42
|
import { defineConfig } from '@wolfstar/http-framework/config';
|
|
43
43
|
|
|
44
|
-
export default defineConfig({
|
|
45
|
-
entry: 'src/main.ts',
|
|
46
|
-
build: { tool: 'tsdown' }
|
|
47
|
-
});
|
|
44
|
+
export default defineConfig({});
|
|
48
45
|
```
|
|
49
46
|
|
|
50
|
-
`stars dev`'s URL needs no configuration either — it is detected from `HTTP_PORT` (env var,
|
|
47
|
+
`stars dev`'s URL needs no configuration either — it is detected from `HTTP_PORT` (env var, `src/.env*`/`.env*`, or `dev.env`) or `3000`, the same way Vite's and Nuxt's dev servers do, and `localhost` is swapped for `127.0.0.1` at runtime if that is what is actually reachable. Set `dev.url` only to override it.
|
|
51
48
|
|
|
52
49
|
The resolved configuration is also available programmatically, exactly as the commands see it (re-exported from this package for convenience, or import it directly from `@wolfstar/http-framework/config`):
|
|
53
50
|
|
|
@@ -74,23 +71,54 @@ stars --help | --version
|
|
|
74
71
|
|
|
75
72
|
Watches the sources through the configured build tool (`tsdown` programmatically, configured from your `stars.config`, `tsc -b --watch`, or a plain file watcher for JavaScript projects), starts the bot after the first successful build and restarts it after every following one. Failed builds keep the previous process running and wait for the next change; a crashed bot waits for the next change or a manual restart.
|
|
76
73
|
|
|
77
|
-
The bot runs as a child `node` process with `STARS_DEV=1` in its environment. Because `stars dev` already restarts the whole process, leave the framework's own `hmr` option disabled while using it.
|
|
74
|
+
The bot runs as a child `node` process with `STARS_DEV=1` and `NODE_ENV=development` in its environment. Because `stars dev` already restarts the whole process, leave the framework's own `hmr` option disabled while using it.
|
|
75
|
+
|
|
76
|
+
**Interactive UI** (default on a TTY): a bottom-aligned panel following the layout and keyboard conventions of
|
|
77
|
+
[Nuxt CLI's dev TUI](https://github.com/nuxt/cli/tree/b4b366eafdd9ac4d5b81b6ae7dadda35364252c9/packages/nuxt-cli/src/dev/tui).
|
|
78
|
+
The normal screen shows a Stars wordmark, aligned URLs, a 20-cell progress bar with elapsed time, status and
|
|
79
|
+
shortcuts. The percentage follows actual build milestones, not a timer: it can stay still while a compiler phase
|
|
80
|
+
runs. Once ready, the bar gives way to diagnostics and the header reports the load time.
|
|
81
|
+
|
|
82
|
+
Application output (including its banner), build-plugin output and diagnostics stay in the bounded log history and
|
|
83
|
+
`.stars/dev.log`. tsdown's entry list and output-size table are suppressed. Only log/help/info overlays enter the
|
|
84
|
+
alternate screen; closing them restores the panel without duplicating output in scrollback. Error stack frames do
|
|
85
|
+
not count as individual errors. `READY` reports process state unless `dev.health` is configured; it does not certify
|
|
86
|
+
that every application plugin loaded successfully. Logged errors switch the badge to `ERROR`.
|
|
87
|
+
|
|
88
|
+
| Key | Action |
|
|
89
|
+
| -------------- | ------------------------------------------------------------ |
|
|
90
|
+
| `r` / `Ctrl+R` | restart the bot |
|
|
91
|
+
| `o` | open the local URL in a browser |
|
|
92
|
+
| `t` | toggle a public `cloudflared` tunnel |
|
|
93
|
+
| `i` | show project, versions, URLs, health, types and session info |
|
|
94
|
+
| `l` | browse logs |
|
|
95
|
+
| `e` | select the last error with its surrounding context |
|
|
96
|
+
| `c` / `Ctrl+L` | clear log history |
|
|
97
|
+
| `h` / `?` | show keyboard shortcuts |
|
|
98
|
+
| `q` / `Ctrl+D` | quit; confirm with `y` while a build/restart is in flight |
|
|
99
|
+
| `Ctrl+C` | quit immediately from any view |
|
|
100
|
+
|
|
101
|
+
In the log view: arrows or `j/k` select, `PgUp/PgDn` move a page, `g/G` go to the beginning/follow the tail,
|
|
102
|
+
`e/w/a` filter errors/warnings/all, `c/b/r` toggle CLI/build/runtime sources, `/` searches, `x` clears, and
|
|
103
|
+
`Enter`/`y` copies the selected line on terminals supporting OSC 52 clipboard writes. `q`, `Esc` or the view's
|
|
104
|
+
own shortcut closes an overlay rather than quitting the session. Nuxt-specific request and page-route inspectors
|
|
105
|
+
are not exposed: the bot supervisor does not receive those runtime events.
|
|
106
|
+
|
|
107
|
+
Replace the default wordmark in `stars.config.ts` (up to four lines are displayed, clipped to the terminal width):
|
|
78
108
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
109
|
+
```ts
|
|
110
|
+
export default defineConfig({
|
|
111
|
+
dev: { banner: ['★ STARYL', 'Twitch notifications'] }
|
|
112
|
+
});
|
|
113
|
+
```
|
|
82
114
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
| `r` | restart the bot |
|
|
86
|
-
| `c` | clear the logs |
|
|
87
|
-
| `f` | cycle the source filter (all › app › build › stars) |
|
|
88
|
-
| `e` | cycle the level filter (all › warnings › errors) |
|
|
89
|
-
| `↑` `↓` / `j` `k` | scroll one line (`PgUp`/`PgDn` one page, `End` to follow) |
|
|
90
|
-
| `h` / `?` | toggle help |
|
|
91
|
-
| `q` / `Esc` / `Ctrl+C` | quit |
|
|
115
|
+
`dev.banner` also accepts a string containing newlines, or `false` to hide the wordmark. Omit it for Stars branding.
|
|
116
|
+
For the application's standalone banner outside the TUI, use `createStarsBanner` from `@wolfstar/start-banner`.
|
|
92
117
|
|
|
93
|
-
**Plain mode** prints prefixed lines instead and is selected by `--no-tui`, `STARS_TUI=plain`,
|
|
118
|
+
**Plain mode** prints prefixed lines instead and is selected by `--no-tui`, `STARS_TUI=plain`, redirected input/output,
|
|
119
|
+
CI, `TERM=dumb`, or terminals smaller than 40×10. `STARS_TUI=1` overrides CI/size checks, never redirected streams or
|
|
120
|
+
a dumb terminal. Both modes honour `NO_COLOR`/`FORCE_COLOR`; `STARS_REDUCED_MOTION=1` freezes the logo/spinner but
|
|
121
|
+
keeps the elapsed clock. Both stop the bot cleanly on `SIGINT`/`SIGTERM`. `SIGUSR2` restarts the bot (not on Windows).
|
|
94
122
|
|
|
95
123
|
### `stars commands`
|
|
96
124
|
|
|
@@ -120,7 +148,7 @@ Three `dev` options round out the dev loop (all documented in
|
|
|
120
148
|
`dev.typecheck.checker` picks which one: `tsc` (the project's TypeScript, watch mode), `golar` (`golar tsc`, watch
|
|
121
149
|
mode), `tsz` (the tsc-compatible checker, re-run after every build since it has no watch mode), or `auto` — the
|
|
122
150
|
default, which uses `golar` when the project depends on it and `tsc` otherwise.
|
|
123
|
-
-
|
|
151
|
+
- Pressing `t` opens and closes a `cloudflared` quick tunnel without configuration. `dev.tunnel: true` opens it at startup so Discord can reach the bot's interactions endpoint from the
|
|
124
152
|
internet; a string is an https URL you already serve, which the CLI only probes. `dev.tunnel.updateEndpoint` writes
|
|
125
153
|
the URL to the Discord application, and is opt-in because it edits a live application.
|
|
126
154
|
- `dev.logFile` (default `.stars/dev.log`) mirrors the session's logs to disk, so a run can be read back after the
|
|
@@ -131,15 +159,13 @@ Three `dev` options round out the dev loop (all documented in
|
|
|
131
159
|
`tsdown` is configured from `stars.config`, and a base project configures nothing: the entry's directory, one output
|
|
132
160
|
file per source file, ESM on Node, `build.outDir`, the tsconfig (`src/tsconfig.json` or `tsconfig.json`), the
|
|
133
161
|
extension `build.output` implies, sourcemaps, unbundled dependencies, Nuxt's `~`/`@`/`~~`/`@@` alias prefixes and
|
|
134
|
-
the auto imports plugin are all filled in
|
|
162
|
+
the auto imports plugin, and copying `src/locales` to `dist/locales` are all filled in
|
|
135
163
|
(the [framework README](../http-framework#the-build-tsdown) lists every default). The `tsdown` block is for what they
|
|
136
164
|
cannot know:
|
|
137
165
|
|
|
138
166
|
```typescript
|
|
139
167
|
export default defineConfig({
|
|
140
|
-
|
|
141
|
-
future: { compatibilityVersion: 4 },
|
|
142
|
-
tsdown: { plugins: [copyLocales()] }
|
|
168
|
+
tsdown: { dts: true }
|
|
143
169
|
});
|
|
144
170
|
```
|
|
145
171
|
|
|
@@ -150,13 +176,13 @@ and which options the block sets.
|
|
|
150
176
|
|
|
151
177
|
### Compatibility version
|
|
152
178
|
|
|
153
|
-
`future.compatibilityVersion`
|
|
179
|
+
`future.compatibilityVersion` selects the legacy or current defaults, the way Nuxt's own compatibility setting does (see the
|
|
154
180
|
[framework README](../http-framework#compatibility-version) for the full reference):
|
|
155
181
|
|
|
156
182
|
| Version | What it changes |
|
|
157
183
|
| ------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
158
|
-
| `3`
|
|
159
|
-
| `4`
|
|
184
|
+
| `3` | Legacy behaviour: a `tsdown.config.*` drives the build, auto imports off unless asked for |
|
|
185
|
+
| `4` (default) | `tsdown` configured from `stars.config` alone, auto imports on and wired in, `'auto'` picks `tsdown` for TypeScript |
|
|
160
186
|
|
|
161
187
|
### Experimental flags
|
|
162
188
|
|
|
@@ -9,7 +9,7 @@ var build_default = defineCommand({
|
|
|
9
9
|
},
|
|
10
10
|
args: { ...projectArgs },
|
|
11
11
|
async run({ args }) {
|
|
12
|
-
const { runBuild } = await import("./build-
|
|
12
|
+
const { runBuild } = await import("./build-vpcF5bwc.js");
|
|
13
13
|
await runBuild({
|
|
14
14
|
config: args.config,
|
|
15
15
|
cwd: args.cwd
|
|
@@ -19,4 +19,4 @@ var build_default = defineCommand({
|
|
|
19
19
|
|
|
20
20
|
//#endregion
|
|
21
21
|
export { build_default as default };
|
|
22
|
-
//# sourceMappingURL=build-
|
|
22
|
+
//# sourceMappingURL=build-B1YfbqX3.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-
|
|
1
|
+
{"version":3,"file":"build-B1YfbqX3.js","names":[],"sources":["../src/commands/build.ts"],"sourcesContent":["import { defineCommand } from 'citty';\nimport { projectArgs } from '../lib/args.js';\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'build',\n\t\tdescription: 'Build the project once with the configured build tool'\n\t},\n\targs: { ...projectArgs },\n\tasync run({ args }) {\n\t\tconst { runBuild } = await import('../lib/tasks/build.js');\n\t\tawait runBuild({ config: args.config, cwd: args.cwd });\n\t}\n});\n"],"mappings":";;;;AAGA,oBAAe,cAAc;CAC5B,MAAM;EACL,MAAM;EACN,aAAa;CACd;CACA,MAAM,EAAE,GAAG,YAAY;CACvB,MAAM,IAAI,EAAE,QAAQ;EACnB,MAAM,EAAE,aAAa,MAAM,OAAO;EAClC,MAAM,SAAS;GAAE,QAAQ,KAAK;GAAQ,KAAK,KAAK;EAAI,CAAC;CACtD;AACD,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { n as ExitCode, t as CliError } from "./errors-Bket6XFn.js";
|
|
2
|
-
import { i as shouldUseColor } from "./output-mode-
|
|
2
|
+
import { i as shouldUseColor } from "./output-mode-Cxes6YUW.js";
|
|
3
3
|
import { n as resolveCwd } from "./args-Ch1KXUMn.js";
|
|
4
|
-
import { n as createBuilder, t as
|
|
5
|
-
import { t as prepareAutoImports } from "./prepare-
|
|
4
|
+
import { n as assertSupportedExperiments, r as createBuilder, t as Locales } from "./locales-B3mRenhY.js";
|
|
5
|
+
import { t as prepareAutoImports } from "./prepare-CHLrneYU.js";
|
|
6
6
|
import { displayPath, loadStarsConfig } from "@wolfstar/http-framework/config";
|
|
7
7
|
import { createColors } from "colorette";
|
|
8
8
|
|
|
@@ -31,9 +31,10 @@ async function runBuild(options) {
|
|
|
31
31
|
code: "BUILD_FAILED",
|
|
32
32
|
exitCode: ExitCode.BuildFailed
|
|
33
33
|
});
|
|
34
|
+
new Locales(config).copy();
|
|
34
35
|
stdout.write(`${colors.dim("stars")} ${colors.green(`built in ${outcome.durationMs}ms`)} → ${displayPath(config.root, config.build.output)}\n`);
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
//#endregion
|
|
38
39
|
export { runBuild };
|
|
39
|
-
//# sourceMappingURL=build-
|
|
40
|
+
//# sourceMappingURL=build-vpcF5bwc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-vpcF5bwc.js","names":[],"sources":["../src/lib/tasks/build.ts"],"sourcesContent":["import { createColors } from 'colorette';\nimport type { ProjectArgs } from '../args.js';\nimport { resolveCwd } from '../args.js';\nimport { assertSupportedExperiments, createBuilder } from '../builders/index.js';\nimport { loadStarsConfig } from '@wolfstar/http-framework/config';\nimport { displayPath } from '@wolfstar/http-framework/config';\nimport { CliError, ExitCode } from '../errors.js';\nimport { shouldUseColor } from '../output-mode.js';\nimport { Locales } from '../locales.js';\nimport { prepareAutoImports } from './prepare.js';\n\nexport interface BuildTaskOptions extends ProjectArgs {\n\tstdout?: NodeJS.WritableStream;\n}\n\nexport async function runBuild(options: BuildTaskOptions): Promise<void> {\n\tconst stdout = options.stdout ?? process.stdout;\n\tconst colors = createColors({ useColor: shouldUseColor() });\n\tconst config = await loadStarsConfig({ cwd: resolveCwd(options), configFile: options.config });\n\tassertSupportedExperiments(config);\n\tawait prepareAutoImports(config);\n\n\tif (config.build.tool === 'none') {\n\t\tstdout.write(`${colors.dim('stars')} nothing to build, ${displayPath(config.root, config.entry)} runs as-is (build.tool is 'none')\\n`);\n\t\treturn;\n\t}\n\n\tconst builder = await createBuilder(config);\n\tbuilder.on('log', (level, text) => {\n\t\tconst paint = level === 'error' ? colors.red : level === 'warn' ? colors.yellow : (value: string) => value;\n\t\tstdout.write(`${paint(text)}\\n`);\n\t});\n\n\tstdout.write(`${colors.dim('stars')} building with ${colors.bold(config.build.tool)}…\\n`);\n\tconst outcome = await builder.build();\n\tif (!outcome.ok) {\n\t\tthrow new CliError(`Build failed${outcome.message ? `: ${outcome.message}` : ''}`, { code: 'BUILD_FAILED', exitCode: ExitCode.BuildFailed });\n\t}\n\tnew Locales(config).copy();\n\n\tstdout.write(`${colors.dim('stars')} ${colors.green(`built in ${outcome.durationMs}ms`)} → ${displayPath(config.root, config.build.output)}\\n`);\n}\n"],"mappings":";;;;;;;;;AAeA,eAAsB,SAAS,SAA0C;CACxE,MAAM,SAAS,QAAQ,UAAU,QAAQ;CACzC,MAAM,SAAS,aAAa,EAAE,UAAU,eAAe,EAAE,CAAC;CAC1D,MAAM,SAAS,MAAM,gBAAgB;EAAE,KAAK,WAAW,OAAO;EAAG,YAAY,QAAQ;CAAO,CAAC;CAC7F,2BAA2B,MAAM;CACjC,MAAM,mBAAmB,MAAM;CAE/B,IAAI,OAAO,MAAM,SAAS,QAAQ;EACjC,OAAO,MAAM,GAAG,OAAO,IAAI,OAAO,EAAE,qBAAqB,YAAY,OAAO,MAAM,OAAO,KAAK,EAAE,qCAAqC;EACrI;CACD;CAEA,MAAM,UAAU,MAAM,cAAc,MAAM;CAC1C,QAAQ,GAAG,QAAQ,OAAO,SAAS;EAClC,MAAM,QAAQ,UAAU,UAAU,OAAO,MAAM,UAAU,SAAS,OAAO,UAAU,UAAkB;EACrG,OAAO,MAAM,GAAG,MAAM,IAAI,EAAE,GAAG;CAChC,CAAC;CAED,OAAO,MAAM,GAAG,OAAO,IAAI,OAAO,EAAE,iBAAiB,OAAO,KAAK,OAAO,MAAM,IAAI,EAAE,IAAI;CACxF,MAAM,UAAU,MAAM,QAAQ,MAAM;CACpC,IAAI,CAAC,QAAQ,IACZ,MAAM,IAAI,SAAS,eAAe,QAAQ,UAAU,KAAK,QAAQ,YAAY,MAAM;EAAE,MAAM;EAAgB,UAAU,SAAS;CAAY,CAAC;CAE5I,IAAI,QAAQ,MAAM,CAAC,CAAC,KAAK;CAEzB,OAAO,MAAM,GAAG,OAAO,IAAI,OAAO,EAAE,GAAG,OAAO,MAAM,YAAY,QAAQ,WAAW,GAAG,EAAE,KAAK,YAAY,OAAO,MAAM,OAAO,MAAM,MAAM,EAAE,GAAG;AAC/I"}
|
package/dist/cli.js
CHANGED
|
@@ -6,12 +6,12 @@ import { defineCommand, renderUsage, runCommand } from "citty";
|
|
|
6
6
|
//#region src/cli.ts
|
|
7
7
|
const packageJson = readOwnPackageJson();
|
|
8
8
|
const subCommands = {
|
|
9
|
-
dev: () => import("./dev-
|
|
10
|
-
build: () => import("./build-
|
|
11
|
-
info: () => import("./info-
|
|
12
|
-
codegen: () => import("./codegen-
|
|
13
|
-
prepare: () => import("./prepare-
|
|
14
|
-
commands: () => import("./commands-
|
|
9
|
+
dev: () => import("./dev-hT1WR1Kh.js").then((module) => module.default),
|
|
10
|
+
build: () => import("./build-B1YfbqX3.js").then((module) => module.default),
|
|
11
|
+
info: () => import("./info-rYRLITnv.js").then((module) => module.default),
|
|
12
|
+
codegen: () => import("./codegen-1kW_OnrL.js").then((module) => module.default),
|
|
13
|
+
prepare: () => import("./prepare-hOwjyelr.js").then((module) => module.default),
|
|
14
|
+
commands: () => import("./commands-hpnmaBM2.js").then((module) => module.default)
|
|
15
15
|
};
|
|
16
16
|
const main = defineCommand({
|
|
17
17
|
meta: {
|
|
@@ -21,7 +21,7 @@ var codegen_default = defineCommand({
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
async run({ args }) {
|
|
24
|
-
const { runCodegen } = await import("./codegen-
|
|
24
|
+
const { runCodegen } = await import("./codegen-DHQH83M3.js");
|
|
25
25
|
await runCodegen({
|
|
26
26
|
config: args.config,
|
|
27
27
|
cwd: args.cwd,
|
|
@@ -33,4 +33,4 @@ var codegen_default = defineCommand({
|
|
|
33
33
|
|
|
34
34
|
//#endregion
|
|
35
35
|
export { codegen_default as default };
|
|
36
|
-
//# sourceMappingURL=codegen-
|
|
36
|
+
//# sourceMappingURL=codegen-1kW_OnrL.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codegen-
|
|
1
|
+
{"version":3,"file":"codegen-1kW_OnrL.js","names":[],"sources":["../src/commands/codegen.ts"],"sourcesContent":["import { defineCommand } from 'citty';\nimport { projectArgs } from '../lib/args.js';\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'codegen',\n\t\tdescription: 'Run the configured code generators (i18next types)'\n\t},\n\targs: {\n\t\t...projectArgs,\n\t\tcheck: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Fail when the generated files are out of date instead of writing them',\n\t\t\tdefault: false\n\t\t},\n\t\tjson: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Print machine-readable JSON',\n\t\t\tdefault: false\n\t\t}\n\t},\n\tasync run({ args }) {\n\t\tconst { runCodegen } = await import('../lib/tasks/codegen.js');\n\t\tawait runCodegen({ config: args.config, cwd: args.cwd, check: args.check, json: args.json });\n\t}\n});\n"],"mappings":";;;;AAGA,sBAAe,cAAc;CAC5B,MAAM;EACL,MAAM;EACN,aAAa;CACd;CACA,MAAM;EACL,GAAG;EACH,OAAO;GACN,MAAM;GACN,aAAa;GACb,SAAS;EACV;EACA,MAAM;GACL,MAAM;GACN,aAAa;GACb,SAAS;EACV;CACD;CACA,MAAM,IAAI,EAAE,QAAQ;EACnB,MAAM,EAAE,eAAe,MAAM,OAAO;EACpC,MAAM,WAAW;GAAE,QAAQ,KAAK;GAAQ,KAAK,KAAK;GAAK,OAAO,KAAK;GAAO,MAAM,KAAK;EAAK,CAAC;CAC5F;AACD,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as ExitCode, t as CliError } from "./errors-Bket6XFn.js";
|
|
2
|
-
import { i as shouldUseColor } from "./output-mode-
|
|
2
|
+
import { i as shouldUseColor } from "./output-mode-Cxes6YUW.js";
|
|
3
3
|
import { n as resolveCwd } from "./args-Ch1KXUMn.js";
|
|
4
4
|
import { i as resolveFromProject } from "./project-DHF3qdm-.js";
|
|
5
5
|
import { displayPath, loadStarsConfig } from "@wolfstar/http-framework/config";
|
|
@@ -92,4 +92,4 @@ function generate(root, cli, locales, output) {
|
|
|
92
92
|
|
|
93
93
|
//#endregion
|
|
94
94
|
export { runCodegen };
|
|
95
|
-
//# sourceMappingURL=codegen-
|
|
95
|
+
//# sourceMappingURL=codegen-DHQH83M3.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codegen-
|
|
1
|
+
{"version":3,"file":"codegen-DHQH83M3.js","names":[],"sources":["../src/lib/tasks/codegen.ts"],"sourcesContent":["import { createColors } from 'colorette';\nimport { spawn } from 'node:child_process';\nimport { mkdtemp, readFile, rm } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport type { ProjectArgs } from '../args.js';\nimport { resolveCwd } from '../args.js';\nimport { loadStarsConfig } from '@wolfstar/http-framework/config';\nimport { displayPath, type ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { CliError, ExitCode } from '../errors.js';\nimport { shouldUseColor } from '../output-mode.js';\nimport { resolveFromProject } from '../project.js';\n\nexport interface CodegenTaskOptions extends ProjectArgs {\n\tcheck?: boolean;\n\tjson?: boolean;\n\tstdout?: NodeJS.WritableStream;\n}\n\nexport interface CodegenResult {\n\tgenerator: 'i18n';\n\toutput: string;\n\tstatus: 'written' | 'up-to-date' | 'outdated';\n}\n\nexport async function runCodegen(options: CodegenTaskOptions): Promise<void> {\n\tconst stdout = options.stdout ?? process.stdout;\n\tconst colors = createColors({ useColor: shouldUseColor() && !options.json });\n\tconst config = await loadStarsConfig({ cwd: resolveCwd(options), configFile: options.config });\n\tconst results: CodegenResult[] = [];\n\n\tif (config.codegen.i18n) results.push(await runI18n(config, Boolean(options.check)));\n\n\tif (options.json) {\n\t\tstdout.write(`${JSON.stringify({ check: Boolean(options.check), results }, null, 2)}\\n`);\n\t} else if (results.length === 0) {\n\t\tstdout.write(`${colors.dim('stars')} nothing to generate (no code generators are configured)\\n`);\n\t} else {\n\t\tfor (const result of results) {\n\t\t\tconst paint = result.status === 'outdated' ? colors.red : colors.green;\n\t\t\tstdout.write(`${colors.dim('stars')} ${result.generator}: ${paint(result.status)} ${displayPath(config.root, result.output)}\\n`);\n\t\t}\n\t}\n\n\tif (results.some((result) => result.status === 'outdated')) {\n\t\tthrow new CliError('Generated files are out of date, run `stars codegen` to update them.', {\n\t\t\tcode: 'CODEGEN_OUTDATED',\n\t\t\texitCode: ExitCode.Error\n\t\t});\n\t}\n}\n\nasync function runI18n(config: ResolvedStarsConfig, check: boolean): Promise<CodegenResult> {\n\tconst { locales, output } = config.codegen.i18n!;\n\tconst cli = resolveFromProject(config.root, '@wolfstar/i18next-type-generator');\n\tif (!cli) {\n\t\tthrow new CliError(`\"@wolfstar/i18next-type-generator\" is not installed in ${config.root}`, {\n\t\t\tcode: 'DEPENDENCY_MISSING',\n\t\t\thint: 'Install it with `pnpm add -D @wolfstar/i18next-type-generator`, or set `codegen.i18n` to false.'\n\t\t});\n\t}\n\n\tif (!check) {\n\t\tawait generate(config.root, cli, locales, output);\n\t\treturn { generator: 'i18n', output, status: 'written' };\n\t}\n\n\tconst directory = await mkdtemp(join(tmpdir(), 'stars-codegen-'));\n\ttry {\n\t\tconst candidate = join(directory, 'i18next.d.ts');\n\t\tawait generate(config.root, cli, locales, candidate);\n\t\tconst [expected, actual] = await Promise.all([readFile(candidate, 'utf-8'), readFile(output, 'utf-8').catch(() => null)]);\n\t\treturn { generator: 'i18n', output, status: expected === actual ? 'up-to-date' : 'outdated' };\n\t} finally {\n\t\tawait rm(directory, { recursive: true, force: true });\n\t}\n}\n\nfunction generate(root: string, cli: string, locales: string, output: string): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst child = spawn(process.execPath, [cli, locales, output], { cwd: root, stdio: ['ignore', 'ignore', 'pipe'], windowsHide: true });\n\t\tlet stderr = '';\n\t\tchild.stderr?.on('data', (chunk: Buffer) => (stderr += chunk.toString()));\n\t\tchild.once('error', reject);\n\t\tchild.once('exit', (code) => {\n\t\t\tif (code === 0) resolve();\n\t\t\telse\n\t\t\t\treject(\n\t\t\t\t\tnew CliError(`i18next-type-generator exited with code ${code}${stderr ? `: ${stderr.trim()}` : ''}`, { code: 'CODEGEN_FAILED' })\n\t\t\t\t);\n\t\t});\n\t});\n}\n"],"mappings":";;;;;;;;;;;;AAyBA,eAAsB,WAAW,SAA4C;CAC5E,MAAM,SAAS,QAAQ,UAAU,QAAQ;CACzC,MAAM,SAAS,aAAa,EAAE,UAAU,eAAe,KAAK,CAAC,QAAQ,KAAK,CAAC;CAC3E,MAAM,SAAS,MAAM,gBAAgB;EAAE,KAAK,WAAW,OAAO;EAAG,YAAY,QAAQ;CAAO,CAAC;CAC7F,MAAM,UAA2B,CAAC;CAElC,IAAI,OAAO,QAAQ,MAAM,QAAQ,KAAK,MAAM,QAAQ,QAAQ,QAAQ,QAAQ,KAAK,CAAC,CAAC;CAEnF,IAAI,QAAQ,MACX,OAAO,MAAM,GAAG,KAAK,UAAU;EAAE,OAAO,QAAQ,QAAQ,KAAK;EAAG;CAAQ,GAAG,MAAM,CAAC,EAAE,GAAG;MACjF,IAAI,QAAQ,WAAW,GAC7B,OAAO,MAAM,GAAG,OAAO,IAAI,OAAO,EAAE,2DAA2D;MAE/F,KAAK,MAAM,UAAU,SAAS;EAC7B,MAAM,QAAQ,OAAO,WAAW,aAAa,OAAO,MAAM,OAAO;EACjE,OAAO,MAAM,GAAG,OAAO,IAAI,OAAO,EAAE,GAAG,OAAO,UAAU,IAAI,MAAM,OAAO,MAAM,EAAE,GAAG,YAAY,OAAO,MAAM,OAAO,MAAM,EAAE,GAAG;CAChI;CAGD,IAAI,QAAQ,MAAM,WAAW,OAAO,WAAW,UAAU,GACxD,MAAM,IAAI,SAAS,wEAAwE;EAC1F,MAAM;EACN,UAAU,SAAS;CACpB,CAAC;AAEH;AAEA,eAAe,QAAQ,QAA6B,OAAwC;CAC3F,MAAM,EAAE,SAAS,WAAW,OAAO,QAAQ;CAC3C,MAAM,MAAM,mBAAmB,OAAO,MAAM,kCAAkC;CAC9E,IAAI,CAAC,KACJ,MAAM,IAAI,SAAS,0DAA0D,OAAO,QAAQ;EAC3F,MAAM;EACN,MAAM;CACP,CAAC;CAGF,IAAI,CAAC,OAAO;EACX,MAAM,SAAS,OAAO,MAAM,KAAK,SAAS,MAAM;EAChD,OAAO;GAAE,WAAW;GAAQ;GAAQ,QAAQ;EAAU;CACvD;CAEA,MAAM,YAAY,MAAM,QAAQ,KAAK,OAAO,GAAG,gBAAgB,CAAC;CAChE,IAAI;EACH,MAAM,YAAY,KAAK,WAAW,cAAc;EAChD,MAAM,SAAS,OAAO,MAAM,KAAK,SAAS,SAAS;EACnD,MAAM,CAAC,UAAU,UAAU,MAAM,QAAQ,IAAI,CAAC,SAAS,WAAW,OAAO,GAAG,SAAS,QAAQ,OAAO,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC;EACxH,OAAO;GAAE,WAAW;GAAQ;GAAQ,QAAQ,aAAa,SAAS,eAAe;EAAW;CAC7F,UAAU;EACT,MAAM,GAAG,WAAW;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;CACrD;AACD;AAEA,SAAS,SAAS,MAAc,KAAa,SAAiB,QAA+B;CAC5F,OAAO,IAAI,SAAS,SAAS,WAAW;EACvC,MAAM,QAAQ,MAAM,QAAQ,UAAU;GAAC;GAAK;GAAS;EAAM,GAAG;GAAE,KAAK;GAAM,OAAO;IAAC;IAAU;IAAU;GAAM;GAAG,aAAa;EAAK,CAAC;EACnI,IAAI,SAAS;EACb,MAAM,QAAQ,GAAG,SAAS,UAAmB,UAAU,MAAM,SAAS,CAAE;EACxE,MAAM,KAAK,SAAS,MAAM;EAC1B,MAAM,KAAK,SAAS,SAAS;GAC5B,IAAI,SAAS,GAAG,QAAQ;QAEvB,OACC,IAAI,SAAS,2CAA2C,OAAO,SAAS,KAAK,OAAO,KAAK,MAAM,MAAM,EAAE,MAAM,iBAAiB,CAAC,CAChI;EACF,CAAC;CACF,CAAC;AACF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as CliError } from "./errors-Bket6XFn.js";
|
|
2
|
-
import { i as shouldUseColor } from "./output-mode-
|
|
2
|
+
import { i as shouldUseColor } from "./output-mode-Cxes6YUW.js";
|
|
3
3
|
import { n as resolveCwd } from "./args-Ch1KXUMn.js";
|
|
4
|
-
import { n as readDiscordCredentials } from "./tunnel-
|
|
4
|
+
import { n as readDiscordCredentials } from "./tunnel-mGJe2zuE.js";
|
|
5
5
|
import { loadStarsConfig } from "@wolfstar/http-framework/config";
|
|
6
6
|
import { createColors } from "colorette";
|
|
7
7
|
|
|
@@ -87,7 +87,7 @@ function createClackPrompt() {
|
|
|
87
87
|
initialValue: false
|
|
88
88
|
});
|
|
89
89
|
if (isCancel(answer)) throw aborted();
|
|
90
|
-
return answer;
|
|
90
|
+
return Boolean(answer);
|
|
91
91
|
}
|
|
92
92
|
};
|
|
93
93
|
}
|
|
@@ -230,4 +230,4 @@ function confirmationRequired() {
|
|
|
230
230
|
|
|
231
231
|
//#endregion
|
|
232
232
|
export { runCommandsClean, runCommandsList };
|
|
233
|
-
//# sourceMappingURL=commands-
|
|
233
|
+
//# sourceMappingURL=commands-DPGDL7ft.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands-DPGDL7ft.js","names":[],"sources":["../src/lib/discord.ts","../src/lib/prompts.ts","../src/lib/tasks/commands.ts"],"sourcesContent":["import type { ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { CliError } from './errors.js';\nimport { readDiscordCredentials } from './tunnel.js';\n\nconst API = 'https://discord.com/api/v10';\n\nexport interface ApplicationCommand {\n\tid: string;\n\tapplication_id: string;\n\tname: string;\n\tdescription?: string;\n\ttype?: number;\n\tguild_id?: string;\n\tversion?: string;\n}\n\nexport interface DiscordClient {\n\tapplicationId: string;\n\tlistCommands(guildId: string | null): Promise<ApplicationCommand[]>;\n\tdeleteCommand(guildId: string | null, commandId: string): Promise<void>;\n}\n\nexport const COMMAND_TYPE_NAMES: Record<number, string> = { 1: 'chat input', 2: 'user', 3: 'message' };\n\n/**\n * A minimal Discord REST client for what `stars commands` manages. The credentials come from the environment or the\n * project's `.env`, the same place the bot reads them from once it starts.\n */\nexport function createDiscordClient(config: ResolvedStarsConfig, env: NodeJS.ProcessEnv = process.env): DiscordClient {\n\tconst credentials = readDiscordCredentials(config, env);\n\tif (!credentials) {\n\t\tthrow new CliError('DISCORD_TOKEN is not set', {\n\t\t\tcode: 'DISCORD_TOKEN_MISSING',\n\t\t\thint: 'Set DISCORD_TOKEN in the environment or in the project .env file.'\n\t\t});\n\t}\n\n\tif (!credentials.applicationId) {\n\t\tthrow new CliError('The Discord application id is not set', {\n\t\t\tcode: 'DISCORD_APPLICATION_ID_MISSING',\n\t\t\thint: 'Set DISCORD_APPLICATION_ID (or APPLICATION_ID) in the environment or in the project .env file.'\n\t\t});\n\t}\n\n\tconst applicationId = credentials.applicationId;\n\tconst scope = (guildId: string | null) =>\n\t\tguildId ? `/applications/${applicationId}/guilds/${guildId}/commands` : `/applications/${applicationId}/commands`;\n\n\tconst request = async (method: string, path: string): Promise<unknown> => {\n\t\tconst response = await fetch(`${API}${path}`, {\n\t\t\tmethod,\n\t\t\theaders: { authorization: `Bot ${credentials.token}` },\n\t\t\tsignal: AbortSignal.timeout(15_000)\n\t\t});\n\n\t\tif (response.status === 204) return null;\n\n\t\tconst body: unknown = await response.json().catch(() => null);\n\t\tif (!response.ok) {\n\t\t\tconst detail = typeof body === 'object' && body !== null && 'message' in body ? String((body as { message: unknown }).message) : '';\n\t\t\tthrow new CliError(`Discord answered ${response.status}${detail ? `: ${detail}` : ''}`, {\n\t\t\t\tcode: 'DISCORD_REQUEST_FAILED',\n\t\t\t\thint: response.status === 401 ? 'Check DISCORD_TOKEN.' : 'Check the application id and the bot permissions.'\n\t\t\t});\n\t\t}\n\n\t\treturn body;\n\t};\n\n\treturn {\n\t\tapplicationId,\n\t\tasync listCommands(guildId) {\n\t\t\tconst body = await request('GET', scope(guildId));\n\t\t\treturn Array.isArray(body) ? (body as ApplicationCommand[]) : [];\n\t\t},\n\t\tasync deleteCommand(guildId, commandId) {\n\t\t\tawait request('DELETE', `${scope(guildId)}/${commandId}`);\n\t\t}\n\t};\n}\n","import type { ApplicationCommand } from './discord.js';\nimport { CliError } from './errors.js';\n\nexport interface CommandsPrompt {\n\t/** Asks which of the deployed commands to delete. Returns the chosen ids. */\n\tpick(commands: readonly ApplicationCommand[], scope: string): Promise<string[]>;\n\t/** Final go/no-go before anything is deleted. */\n\tconfirm(message: string): Promise<boolean>;\n}\n\n/**\n * The interactive wizard behind `stars commands clean`: a checklist of what Discord has deployed, then a\n * confirmation, so a destructive cleanup is never one keystroke away. `@clack/prompts` is loaded lazily — the\n * non-interactive paths (`--yes`, `--json`, CI) never pay for it.\n */\nexport function createClackPrompt(): CommandsPrompt {\n\treturn {\n\t\tasync pick(commands, scope) {\n\t\t\tconst { isCancel, multiselect } = await import('@clack/prompts');\n\t\t\tconst answer = await multiselect({\n\t\t\t\tmessage: `Which commands should be deleted from ${scope}?`,\n\t\t\t\toptions: commands.map((command) => ({\n\t\t\t\t\tvalue: command.id,\n\t\t\t\t\tlabel: command.name,\n\t\t\t\t\thint: [describeType(command), command.id].filter(Boolean).join(' · ')\n\t\t\t\t})),\n\t\t\t\trequired: false\n\t\t\t});\n\n\t\t\tif (isCancel(answer)) throw aborted();\n\t\t\treturn answer as string[];\n\t\t},\n\t\tasync confirm(message) {\n\t\t\tconst { confirm, isCancel } = await import('@clack/prompts');\n\t\t\tconst answer = await confirm({ message, initialValue: false });\n\n\t\t\tif (isCancel(answer)) throw aborted();\n\t\t\treturn Boolean(answer);\n\t\t}\n\t};\n}\n\nfunction describeType(command: ApplicationCommand): string {\n\tswitch (command.type) {\n\t\tcase 2:\n\t\t\treturn 'user';\n\t\tcase 3:\n\t\t\treturn 'message';\n\t\tdefault:\n\t\t\treturn 'chat input';\n\t}\n}\n\nfunction aborted(): CliError {\n\treturn new CliError('Aborted', { code: 'ABORTED' });\n}\n","import { loadStarsConfig } from '@wolfstar/http-framework/config';\nimport { createColors } from 'colorette';\nimport type { ProjectArgs } from '../args.js';\nimport { resolveCwd } from '../args.js';\nimport { COMMAND_TYPE_NAMES, createDiscordClient, type ApplicationCommand, type DiscordClient } from '../discord.js';\nimport { CliError } from '../errors.js';\nimport { shouldUseColor } from '../output-mode.js';\nimport { createClackPrompt, type CommandsPrompt } from '../prompts.js';\n\nexport interface CommandsTaskOptions extends ProjectArgs {\n\t/** A guild id to work on the guild-scoped commands instead of the global ones. */\n\tguild?: string;\n\tjson?: boolean;\n\tstdout?: NodeJS.WritableStream;\n\t/** Overrides the client, for tests. */\n\tclient?: DiscordClient;\n}\n\nexport interface CommandsCleanOptions extends CommandsTaskOptions {\n\t/** Names to delete; without any, the wizard asks which of the deployed commands to remove. */\n\tnames?: string[];\n\t/** Deletes without asking. Required in a non-interactive terminal. */\n\tyes?: boolean;\n\tstdin?: NodeJS.ReadableStream & { isTTY?: boolean };\n\t/** Overrides the interactive wizard, for tests. */\n\tprompt?: CommandsPrompt;\n}\n\n/**\n * Lists the application commands Discord currently has deployed, which is not necessarily what the project would\n * register today: renamed and removed commands stay until something deletes them.\n */\nexport async function runCommandsList(options: CommandsTaskOptions): Promise<void> {\n\tconst stdout = options.stdout ?? process.stdout;\n\tconst colors = createColors({ useColor: shouldUseColor() && !options.json });\n\tconst { client, guild } = await connect(options);\n\tconst commands = await client.listCommands(guild);\n\n\tif (options.json) {\n\t\tstdout.write(`${JSON.stringify({ applicationId: client.applicationId, guildId: guild, commands }, null, 2)}\\n`);\n\t\treturn;\n\t}\n\n\tif (commands.length === 0) {\n\t\tstdout.write(`${colors.dim('stars')} no ${guild ? `commands in guild ${guild}` : 'global commands'} are deployed\\n`);\n\t\treturn;\n\t}\n\n\tstdout.write(`${colors.dim('stars')} ${commands.length} ${guild ? `command(s) in guild ${guild}` : 'global command(s)'}\\n`);\n\tfor (const command of commands) stdout.write(` ${colors.bold(describeName(command))} ${colors.dim(command.id)}\\n`);\n}\n\n/**\n * Deletes deployed application commands. Discord keeps whatever was registered last, so this is the way to clear\n * commands a project no longer defines (or a whole guild's test deployment).\n */\nexport async function runCommandsClean(options: CommandsCleanOptions): Promise<void> {\n\tconst stdout = options.stdout ?? process.stdout;\n\tconst colors = createColors({ useColor: shouldUseColor() && !options.json });\n\tconst { client, guild } = await connect(options);\n\tconst deployed = await client.listCommands(guild);\n\tconst wanted = options.names?.filter((name) => name.length > 0) ?? [];\n\n\tconst missing = wanted.filter((name) => !deployed.some((command) => command.name === name));\n\tif (missing.length > 0) {\n\t\tthrow new CliError(`No deployed command is named ${missing.join(', ')}`, {\n\t\t\tcode: 'COMMAND_NOT_FOUND',\n\t\t\thint: 'Run `stars commands list` to see what is deployed.'\n\t\t});\n\t}\n\n\tif (deployed.length === 0) {\n\t\tif (options.json) stdout.write(`${JSON.stringify({ deleted: [] }, null, 2)}\\n`);\n\t\telse stdout.write(`${colors.dim('stars')} nothing to delete\\n`);\n\t\treturn;\n\t}\n\n\tconst scope = guild ? `guild ${guild}` : 'the global scope';\n\tconst targets = await selectTargets(options, deployed, wanted, scope, stdout, colors);\n\tif (targets.length === 0) {\n\t\tif (options.json) stdout.write(`${JSON.stringify({ deleted: [] }, null, 2)}\\n`);\n\t\telse stdout.write(`${colors.dim('stars')} nothing to delete\\n`);\n\t\treturn;\n\t}\n\n\tfor (const command of targets) {\n\t\tawait client.deleteCommand(guild, command.id);\n\t\tif (!options.json) stdout.write(`${colors.dim('stars')} ${colors.red('deleted')} ${describeName(command)}\\n`);\n\t}\n\n\tif (options.json) stdout.write(`${JSON.stringify({ deleted: targets.map((command) => ({ id: command.id, name: command.name })) }, null, 2)}\\n`);\n}\n\n/**\n * Works out what to delete: `--name` (and `--yes`) keep the command scriptable, while a bare `stars commands clean`\n * on a terminal runs the wizard — a checklist of what Discord has deployed, then a confirmation.\n */\nasync function selectTargets(\n\toptions: CommandsCleanOptions,\n\tdeployed: ApplicationCommand[],\n\twanted: string[],\n\tscope: string,\n\tstdout: NodeJS.WritableStream,\n\tcolors: ReturnType<typeof createColors>\n): Promise<ApplicationCommand[]> {\n\tif (wanted.length > 0) {\n\t\tconst named = deployed.filter((command) => wanted.includes(command.name));\n\t\tif (options.yes) return named;\n\n\t\tawait confirmOrThrow(options, stdout, colors, `delete ${colors.bold(String(named.length))} command(s) from ${scope}?`);\n\t\treturn named;\n\t}\n\n\tif (options.yes) return deployed;\n\n\tconst stdin = options.stdin ?? process.stdin;\n\tif (!options.prompt && !stdin.isTTY) throw confirmationRequired();\n\n\tconst prompt = options.prompt ?? createClackPrompt();\n\tconst chosen = new Set(await prompt.pick(deployed, scope));\n\tconst targets = deployed.filter((command) => chosen.has(command.id));\n\tif (targets.length === 0) return [];\n\n\tconst names = targets.map((command) => command.name).join(', ');\n\tif (!(await prompt.confirm(`Delete ${targets.length} command(s) from ${scope}: ${names}?`))) {\n\t\tthrow new CliError('Aborted', { code: 'ABORTED' });\n\t}\n\n\treturn targets;\n}\n\nasync function connect(options: CommandsTaskOptions): Promise<{ client: DiscordClient; guild: string | null }> {\n\tconst config = await loadStarsConfig({ cwd: resolveCwd(options), configFile: options.config });\n\treturn { client: options.client ?? createDiscordClient(config), guild: options.guild ?? null };\n}\n\nfunction describeName(command: ApplicationCommand): string {\n\tconst type = command.type && command.type !== 1 ? ` (${COMMAND_TYPE_NAMES[command.type] ?? `type ${command.type}`})` : '';\n\treturn `${command.name}${type}`;\n}\n\n/**\n * Deleting deployed commands is not reversible, so a terminal run asks first; scripts pass `--yes`.\n */\nasync function confirmOrThrow(\n\toptions: CommandsCleanOptions,\n\tstdout: NodeJS.WritableStream,\n\tcolors: ReturnType<typeof createColors>,\n\tquestion: string\n): Promise<void> {\n\tif (options.prompt) {\n\t\tif (await options.prompt.confirm(question)) return;\n\t\tthrow new CliError('Aborted', { code: 'ABORTED' });\n\t}\n\n\tconst stdin = options.stdin ?? process.stdin;\n\tif (!stdin.isTTY) throw confirmationRequired();\n\n\tstdout.write(`${colors.dim('stars')} ${question} [y/N] `);\n\treturn new Promise((resolve, reject) => {\n\t\tstdin.setEncoding?.('utf-8');\n\t\tstdin.once('data', (chunk: string) => {\n\t\t\tstdin.pause?.();\n\t\t\tconst answer = chunk.trim().toLowerCase();\n\t\t\tif (answer === 'y' || answer === 'yes') resolve();\n\t\t\telse reject(new CliError('Aborted', { code: 'ABORTED' }));\n\t\t});\n\t\tstdin.resume?.();\n\t});\n}\n\nfunction confirmationRequired(): CliError {\n\treturn new CliError('Refusing to delete commands without a confirmation', {\n\t\tcode: 'CONFIRMATION_REQUIRED',\n\t\thint: 'Pass --yes to delete them, or --name to pick one, from a script.'\n\t});\n}\n"],"mappings":"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"}
|
|
@@ -21,7 +21,7 @@ const list = defineCommand({
|
|
|
21
21
|
},
|
|
22
22
|
args: scopeArgs,
|
|
23
23
|
async run({ args }) {
|
|
24
|
-
const { runCommandsList } = await import("./commands-
|
|
24
|
+
const { runCommandsList } = await import("./commands-DPGDL7ft.js");
|
|
25
25
|
await runCommandsList({
|
|
26
26
|
config: args.config,
|
|
27
27
|
cwd: args.cwd,
|
|
@@ -49,7 +49,7 @@ const clean = defineCommand({
|
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
51
|
async run({ args }) {
|
|
52
|
-
const { runCommandsClean } = await import("./commands-
|
|
52
|
+
const { runCommandsClean } = await import("./commands-DPGDL7ft.js");
|
|
53
53
|
const names = Array.isArray(args.name) ? args.name : args.name ? [args.name] : [];
|
|
54
54
|
await runCommandsClean({
|
|
55
55
|
config: args.config,
|
|
@@ -73,7 +73,7 @@ var commands_default = defineCommand({
|
|
|
73
73
|
args: scopeArgs,
|
|
74
74
|
async run({ args, rawArgs }) {
|
|
75
75
|
if (rawArgs.some((argument) => argument === "list" || argument === "clean")) return;
|
|
76
|
-
const { runCommandsList } = await import("./commands-
|
|
76
|
+
const { runCommandsList } = await import("./commands-DPGDL7ft.js");
|
|
77
77
|
await runCommandsList({
|
|
78
78
|
config: args.config,
|
|
79
79
|
cwd: args.cwd,
|
|
@@ -85,4 +85,4 @@ var commands_default = defineCommand({
|
|
|
85
85
|
|
|
86
86
|
//#endregion
|
|
87
87
|
export { commands_default as default };
|
|
88
|
-
//# sourceMappingURL=commands-
|
|
88
|
+
//# sourceMappingURL=commands-hpnmaBM2.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands-
|
|
1
|
+
{"version":3,"file":"commands-hpnmaBM2.js","names":[],"sources":["../src/commands/commands.ts"],"sourcesContent":["import { defineCommand } from 'citty';\nimport { projectArgs } from '../lib/args.js';\n\nconst scopeArgs = {\n\t...projectArgs,\n\tguild: {\n\t\ttype: 'string',\n\t\tdescription: 'Work on a guild’s commands instead of the global ones'\n\t},\n\tjson: {\n\t\ttype: 'boolean',\n\t\tdescription: 'Print machine-readable JSON',\n\t\tdefault: false\n\t}\n} as const;\n\nconst list = defineCommand({\n\tmeta: { name: 'list', description: 'List the application commands Discord has deployed' },\n\targs: scopeArgs,\n\tasync run({ args }) {\n\t\tconst { runCommandsList } = await import('../lib/tasks/commands.js');\n\t\tawait runCommandsList({ config: args.config, cwd: args.cwd, guild: args.guild, json: args.json });\n\t}\n});\n\nconst clean = defineCommand({\n\tmeta: { name: 'clean', description: 'Delete deployed application commands (all of them, or the named ones)' },\n\targs: {\n\t\t...scopeArgs,\n\t\tname: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Only delete the command with this name (repeatable)'\n\t\t},\n\t\tyes: {\n\t\t\ttype: 'boolean',\n\t\t\talias: 'y',\n\t\t\tdescription: 'Delete without asking for a confirmation',\n\t\t\tdefault: false\n\t\t}\n\t},\n\tasync run({ args }) {\n\t\tconst { runCommandsClean } = await import('../lib/tasks/commands.js');\n\t\tconst names = Array.isArray(args.name) ? args.name : args.name ? [args.name] : [];\n\t\tawait runCommandsClean({ config: args.config, cwd: args.cwd, guild: args.guild, json: args.json, names, yes: args.yes });\n\t}\n});\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'commands',\n\t\tdescription: 'Inspect and clean the application commands deployed to Discord'\n\t},\n\tsubCommands: { list, clean },\n\targs: scopeArgs,\n\tasync run({ args, rawArgs }) {\n\t\t// `stars commands` with no subcommand lists, the way `git branch` does.\n\t\tif (rawArgs.some((argument) => argument === 'list' || argument === 'clean')) return;\n\n\t\tconst { runCommandsList } = await import('../lib/tasks/commands.js');\n\t\tawait runCommandsList({ config: args.config, cwd: args.cwd, guild: args.guild, json: args.json });\n\t}\n});\n"],"mappings":";;;;AAGA,MAAM,YAAY;CACjB,GAAG;CACH,OAAO;EACN,MAAM;EACN,aAAa;CACd;CACA,MAAM;EACL,MAAM;EACN,aAAa;EACb,SAAS;CACV;AACD;AAEA,MAAM,OAAO,cAAc;CAC1B,MAAM;EAAE,MAAM;EAAQ,aAAa;CAAqD;CACxF,MAAM;CACN,MAAM,IAAI,EAAE,QAAQ;EACnB,MAAM,EAAE,oBAAoB,MAAM,OAAO;EACzC,MAAM,gBAAgB;GAAE,QAAQ,KAAK;GAAQ,KAAK,KAAK;GAAK,OAAO,KAAK;GAAO,MAAM,KAAK;EAAK,CAAC;CACjG;AACD,CAAC;AAED,MAAM,QAAQ,cAAc;CAC3B,MAAM;EAAE,MAAM;EAAS,aAAa;CAAwE;CAC5G,MAAM;EACL,GAAG;EACH,MAAM;GACL,MAAM;GACN,aAAa;EACd;EACA,KAAK;GACJ,MAAM;GACN,OAAO;GACP,aAAa;GACb,SAAS;EACV;CACD;CACA,MAAM,IAAI,EAAE,QAAQ;EACnB,MAAM,EAAE,qBAAqB,MAAM,OAAO;EAC1C,MAAM,QAAQ,MAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,OAAO,KAAK,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC;EAChF,MAAM,iBAAiB;GAAE,QAAQ,KAAK;GAAQ,KAAK,KAAK;GAAK,OAAO,KAAK;GAAO,MAAM,KAAK;GAAM;GAAO,KAAK,KAAK;EAAI,CAAC;CACxH;AACD,CAAC;AAED,uBAAe,cAAc;CAC5B,MAAM;EACL,MAAM;EACN,aAAa;CACd;CACA,aAAa;EAAE;EAAM;CAAM;CAC3B,MAAM;CACN,MAAM,IAAI,EAAE,MAAM,WAAW;EAE5B,IAAI,QAAQ,MAAM,aAAa,aAAa,UAAU,aAAa,OAAO,GAAG;EAE7E,MAAM,EAAE,oBAAoB,MAAM,OAAO;EACzC,MAAM,gBAAgB;GAAE,QAAQ,KAAK;GAAQ,KAAK,KAAK;GAAK,OAAO,KAAK;GAAO,MAAM,KAAK;EAAK,CAAC;CACjG;AACD,CAAC"}
|