@memoket-ai/cli 2.0.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/CHANGELOG.md ADDED
@@ -0,0 +1,77 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@memoket-ai/cli` are documented here. Versions follow
4
+ [Semantic Versioning](https://semver.org/).
5
+
6
+ ## [2.0.1] — 2026-07-14
7
+
8
+ ### Fixed
9
+
10
+ - **README had the wrong package name.** The H1 title and the `npm install`
11
+ example both said `@memoket/cli` (typo carried over from the private
12
+ pre-release history). The actual package on npm is `@memoket-ai/cli`.
13
+ Users who copy-pasted the install command from the 2.0.0 README on
14
+ npmjs.org would have hit E404. Both references now point to
15
+ `@memoket-ai/cli`.
16
+ - **LICENSE file is now shipped.** The 2.0.0 tarball didn't include a
17
+ `LICENSE` file (the project was using `"license": "UNLICENSED"` as a
18
+ placeholder). 2.0.1 adds a source-available license file and updates
19
+ `package.json#files` so it ships in the tarball.
20
+
21
+ No code changes. No behavior changes. Bump the patch version because the
22
+ install instructions on the npm package page were broken.
23
+
24
+ ## [2.0.0] — 2026-07-14
25
+
26
+ ### First public release on npm
27
+
28
+ This is the first time `@memoket-ai/cli` ships to the public registry as a
29
+ production package. The 0.2.x development line is collapsed into a clean
30
+ 2.0.0 milestone.
31
+
32
+ ### Changed
33
+
34
+ - **Package visibility**: `publishConfig.access` is now `public` (was
35
+ `restricted`). The package is the first public release on npmjs.org.
36
+ - **No `--provenance`**: the source repository stays private, and npm
37
+ refuses to attach Sigstore provenance from private repos (the
38
+ transparency log is public; attaching a build attestation from a
39
+ private project would leak build metadata). The package is published
40
+ as public, only the source is private.
41
+ - **Local publish blocked** (security): the `publish` and `publish:prod`
42
+ npm scripts have been removed. `node scripts/publish.js` exits with a
43
+ clear error unless `CI=true`, and the production NPM_TOKEN is
44
+ IP-restricted to GitHub Actions' public runner range. Local developers
45
+ can still run `--dry-run` for "what would I publish" sanity checks.
46
+ - **`src/config.js` version constant** bumped to `2.0.0` to match
47
+ `package.json`.
48
+ - **README** version references updated; added a new "NPM_TOKEN
49
+ management" section documenting the token-creation settings, rotation
50
+ schedule, and the two-layer local-publish block.
51
+ - **Release workflow** (`.github/workflows/release.yml`): tag-driven
52
+ releases (e.g. `git tag v2.0.0 && git push origin v2.0.0`) now publish
53
+ with `--access public`.
54
+ - **Dispatch workflow** (`.github/workflows/publish.yml`): manual env +
55
+ tag + dry-run dispatch publishes with `--access public`.
56
+
57
+ ### Security
58
+
59
+ - **`NPM_TOKEN` must be a Granular Access Token** with IP allowlist pinned
60
+ to GitHub Actions' public runner range (`140.82.112.0/20`,
61
+ `4.148.0.0/14`). See `README.md` → "NPM_TOKEN management" for the
62
+ full settings matrix and rotation schedule (90 days).
63
+ - **`scripts/publish.js`** now refuses to run a real publish outside
64
+ `CI=true`. This is a fast local fail-safe complementing the
65
+ IP-restriction on the production token at the npm network layer.
66
+ - **`.gitignore`** explicitly blocks `iam-policies.json`,
67
+ `security-groups.json`, and `*.tgz` from accidental commits. These
68
+ were previously untracked but committable.
69
+
70
+ ### Migration notes for the 0.2.x line
71
+
72
+ - `0.2.1-dev.5` is collapsed into `2.0.0`; no patch is published. If
73
+ you depend on `@memoket-ai/cli@^0.2.0` from a private registry, your
74
+ lockfile may need an `npm install @memoket-ai/cli@latest` after this
75
+ release.
76
+ - `npm run publish` and `npm run publish:prod` are removed; use the
77
+ GitHub Actions dispatch workflow or push a release tag.
package/LICENSE ADDED
@@ -0,0 +1,65 @@
1
+ Memoket CLI Source-Available License
2
+ Copyright (c) 2026 Memoket. All rights reserved.
3
+
4
+ This license governs use of the software distributed as the npm package
5
+ "@memoket-ai/cli" (the "Software"). By installing, copying, or otherwise
6
+ using the Software, you agree to be bound by the terms below.
7
+
8
+ 1. Permitted use.
9
+
10
+ You may use the Software for personal purposes or for the purpose of
11
+ accessing the services operated by Memoket (the "Service"). You may
12
+ install and run the Software on devices you own or control.
13
+
14
+ 2. Restrictions.
15
+
16
+ You may NOT, without prior written permission from Memoket:
17
+
18
+ a. Redistribute the Software, in whole or in part, by any means
19
+ (including but not limited to: re-publishing on npm or any other
20
+ package registry, mirroring, bundling into another product,
21
+ sublicensing, or selling copies).
22
+ b. Modify the Software and redistribute the modified version.
23
+ c. Reverse-engineer, decompile, or disassemble the Software except
24
+ to the extent that such restriction is prohibited by applicable
25
+ law.
26
+ d. Use the Software to build a product or service that competes with
27
+ the Service.
28
+
29
+ 3. Official source of distribution.
30
+
31
+ The only authorized distribution channel for the Software is the
32
+ official npm package "@memoket-ai/cli". Any other copy, in any form,
33
+ is unauthorized.
34
+
35
+ 4. No warranty.
36
+
37
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
38
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
39
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
40
+ NON-INFRINGEMENT. MEMOKET DOES NOT WARRANT THAT THE SOFTWARE WILL BE
41
+ UNINTERRUPTED, ERROR-FREE, OR FREE OF HARMFUL COMPONENTS.
42
+
43
+ 5. Limitation of liability.
44
+
45
+ IN NO EVENT SHALL MEMOKET, ITS AFFILIATES, OFFICERS, DIRECTORS,
46
+ EMPLOYEES, AGENTS, OR LICENSORS BE LIABLE FOR ANY CLAIM, DAMAGES, OR
47
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR
48
+ OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE
49
+ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE, EVEN IF ADVISED OF THE
50
+ POSSIBILITY OF SUCH DAMAGE.
51
+
52
+ 6. Termination.
53
+
54
+ This license terminates automatically if you breach any of its
55
+ terms. Upon termination you must stop using the Software and
56
+ uninstall it from any device on which it is installed.
57
+
58
+ 7. Governing law.
59
+
60
+ This license is governed by the laws of the jurisdiction in which
61
+ Memoket is incorporated, without regard to its conflict-of-laws
62
+ principles.
63
+
64
+ For licensing inquiries (commercial use, redistribution, OEM
65
+ agreements), contact Memoket.
package/README.md ADDED
@@ -0,0 +1,278 @@
1
+ # @memoket-ai/cli
2
+
3
+ Let users onboard the Memoket MCP into their local AI client and log in. The CLI only does **register + login + passthrough**; the tool catalog is always pulled live from the server, so it **never drifts**.
4
+
5
+ > Pure Node.js (ESM), zero native bindings, two runtime dependencies (`commander`, `open`). OAuth: well-known discovery → DCR → PKCE S256 → browser callback.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g @memoket-ai/cli
11
+ ```
12
+
13
+ Then:
14
+
15
+ ```bash
16
+ memoket doctor # connectivity + OAuth discovery
17
+ memoket setup # register into Claude Code / VS Code and log in
18
+ ```
19
+
20
+ ## Commands
21
+
22
+ ```
23
+ memoket setup register MCP into local AI clients (Claude Code / VS Code…) and log in ← most common
24
+ memoket login browser-authorize and save the token (for tools/call)
25
+ memoket tools list tools exposed by the live MCP (tools/list passthrough)
26
+ memoket call <tool> [key=value ...] [--json '{...}'] call a tool (tools/call passthrough)
27
+ memoket endpoints list named endpoints, mark the ACTIVE one
28
+ memoket endpoints use <name> switch the active endpoint (persisted)
29
+ memoket token [--scope a,b] [--name X] [--expires-in 90] issue a Personal API Token (default)
30
+ memoket token --list list API tokens
31
+ memoket token --revoke <id> revoke an API token
32
+ memoket token rotate <id> rotate a token (旧换新: new mtok, old revoked)
33
+ memoket doctor connectivity + OAuth discovery + token login status
34
+ ```
35
+
36
+ Backward-compatible: the old nested forms `memoket token create|list|revoke` and
37
+ `memoket status` all still work (kept as hidden aliases).
38
+ `memoket doctor` now also prints the token login status that `status` used to show.
39
+
40
+ ## Endpoints
41
+
42
+ Each build of the CLI ships with **only its own endpoint** baked in (e.g. a
43
+ `build:dev` / `pack:dev` artifact only knows about `dev`). The active endpoint
44
+ is what's used by every command; the selection is persisted to
45
+ `~/.memoket/config.json`.
46
+
47
+ ```bash
48
+ memoket endpoints # list; the ACTIVE one is marked with *
49
+ memoket endpoints add myenv https://api.example.com # add a custom one
50
+ memoket endpoints use myenv # switch to it (persisted)
51
+ memoket endpoints remove myenv # drop a custom one
52
+ ```
53
+
54
+ A prod-built artifact looks like:
55
+
56
+ ```
57
+ NAME BASE URL ACTIVE
58
+ prod https://mcp.memoket.ai *
59
+ ```
60
+
61
+ Need a different environment? Run `npm run pack:<env>` (see
62
+ [Build targets](#build-targets)) — runtime `memoket endpoints use` only switches
63
+ among what's already baked in plus your customs. To expose more endpoints in
64
+ one binary, edit `config/<env>.json` and rebuild.
65
+
66
+ Global flags: `--url <url>` (one-off endpoint override, wins over the active endpoint) / `--name <name>`.
67
+
68
+ ## Build targets
69
+
70
+ The default active endpoint is decided at build time. `config/<env>.json` holds
71
+ the per-target table (`defaultEndpoints` + `defaultActiveEndpoint`); the build
72
+ script stamps the selected file into `src/config.local.js`, which `src/config.js`
73
+ imports at runtime.
74
+
75
+ **Public npm publishes are prod-only.** `dev` and `test` builds exist for local
76
+ development and internal tarballs; they are never pushed to the public registry.
77
+ The single source of truth for the published artifact is the GitHub release
78
+ workflow (see [Release](#release) below), which always runs `build:prod`
79
+ before `npm publish --access public`.
80
+
81
+ ### Build (bake config, no pack)
82
+
83
+ | Command | baked endpoint | writes to | public? |
84
+ |---|---|---|---|
85
+ | `npm run build:dev` | `dev` | `src/config.local.js` | no — local only |
86
+ | `npm run build:test` | `test` | `src/config.local.js` | no — local only |
87
+ | `npm run build:prod` | `prod` | `src/config.local.js` | yes — release flow bakes this |
88
+ | `npm run build` | `prod` (alias for the publish target) | `src/config.local.js` | yes |
89
+
90
+ ### Pack (bake config + produce flavored tarball)
91
+
92
+ Each `pack:*` runs `build:<env>` first to stamp the right default, then `npm pack
93
+ --ignore-scripts` (so `prepack` doesn't clobber it back to prod). Output:
94
+ `memoket-cli-2.0.0.tgz` in the cwd. Only the **prod** tarball is intended for
95
+ distribution; `pack:dev` / `pack:test` exist so internal staging environments
96
+ can verify a build before the release workflow runs.
97
+
98
+ | Command | baked endpoint | tarball | intended audience |
99
+ |---|---|---|---|
100
+ | `npm run pack:dev` | `dev` | `memoket-cli-2.0.0.tgz` | internal staging only |
101
+ | `npm run pack:test` | `test` | `memoket-cli-2.0.0.tgz` | internal staging only |
102
+ | `npm run pack:prod` | `prod` | `memoket-cli-2.0.0.tgz` | **public (via release workflow)** |
103
+ | `npm run pack` | `prod` (alias) | `memoket-cli-2.0.0.tgz` | **public (via release workflow)** |
104
+
105
+ ### Interactive build
106
+
107
+ If you don't pass an env, `build` prompts for one (TTY only):
108
+
109
+ ```bash
110
+ npm run build # interactive pick → bake src/config.local.js
111
+ ```
112
+
113
+ The prompt lists `1) dev / 2) test / 3) prod`. There is no interactive publish
114
+ — public publishes always go through [Release](#release). For local sanity
115
+ checks before tagging, use `npm run pack:<env> --ignore-scripts` and inspect
116
+ the resulting tarball, or `npm run publish:<env> -- --dry-run` to verify the
117
+ npm view without uploading.
118
+
119
+ `npm test` runs `pretest` → `build:dev` automatically, so the smoke test always
120
+ uses the dev gateway. Local testing uses `pack:<env>` instead of plain
121
+ `npm pack` to choose the flavor. The tarball only contains the one endpoint
122
+ (plus the build script + `config/prod.json`, so consumers can rebuild against
123
+ the prod endpoint or use `memoket --url <host>` for one-off overrides).
124
+
125
+ `src/config.local.js` is gitignored. When it's missing (fresh checkout, no build
126
+ run yet) the CLI falls back to a single prod endpoint so `node bin/memoket.js ...`
127
+ still works out of the box.
128
+
129
+ **Note:** `config/dev.json` and `config/test.json` are intentionally **not**
130
+ shipped in the public tarball — they contain internal staging URLs and live
131
+ only in the source repository. Team members running the CLI from a clone
132
+ already have them via git; consumers installing from npm use `--url` to point
133
+ at non-prod gateways.
134
+
135
+ ## Generic HTTP integrations
136
+
137
+ For n8n, Make, Zapier HTTP Request, scripts, and customer backends, use the generic HTTP integration commands. These are not n8n-specific; they create the same primitives any HTTP client can use.
138
+
139
+ ```bash
140
+ memoket login
141
+
142
+ memoket token \
143
+ --name "My automation" \
144
+ --scope recordings:read,summaries:read,transcripts:read,search:read
145
+ ```
146
+
147
+ The command creates a Personal API Token (`mk_pat_...`) for customer -> Memoket API calls. Use it as:
148
+
149
+ ```http
150
+ Authorization: Bearer mk_pat_...
151
+ ```
152
+
153
+ The underlying management commands are also available directly:
154
+
155
+ ```bash
156
+ memoket token --name "n8n" --scope recordings:read,summaries:read --expires-in 90
157
+ memoket token --list
158
+ memoket token --revoke <id>
159
+ memoket token rotate <id> # 旧换新:签发新 mtok,旧 token 立即失效
160
+ # 旧形式仍兼容:memoket token create ...
161
+ ```
162
+
163
+ These commands target the Gateway REST management API (`/v1/api-tokens`). If the backend endpoint is not deployed yet, the CLI will fail with the returned HTTP status.
164
+
165
+ ## Backend contract
166
+
167
+ The generic HTTP integration commands expect the Memoket REST management API below. The CLI authenticates with the existing `memoket login` OAuth token and sends it as `Authorization: Bearer <token>`.
168
+
169
+ ```http
170
+ POST /v1/api-tokens
171
+ GET /v1/api-tokens
172
+ DELETE /v1/api-tokens/{id}
173
+ ```
174
+
175
+ Create API token request:
176
+
177
+ ```json
178
+ {
179
+ "name": "My automation API",
180
+ "scopes": ["recordings:read", "summaries:read"],
181
+ "expires_in_days": 90
182
+ }
183
+ ```
184
+
185
+ Create API token response must return the cleartext token once, either top-level or under `data`:
186
+
187
+ ```json
188
+ {
189
+ "id": "tok_123",
190
+ "token": "mk_pat_xxx",
191
+ "scopes": ["recordings:read", "summaries:read"],
192
+ "expires_at": "2026-10-04T00:00:00Z"
193
+ }
194
+ ```
195
+
196
+ The CLI derives the API base from the MCP URL. Override with `MEMOKET_API_URL` or `MEMOKET_GATEWAY_URL` during development.
197
+
198
+ ## Why "passthrough" rather than hardcoded commands
199
+
200
+ The MCP's tool catalog is loaded at runtime from the server, not hardcoded. Hardcoding tools in the CLI would drift. This CLI passes `tools/list` / `tools/call` through, so any add/change/drop on the server is followed automatically.
201
+
202
+ ## Build & try from source
203
+
204
+ ```bash
205
+ git clone https://github.com/memoket/memoket-cli.git
206
+ cd memoket-cli
207
+ npm install
208
+ npm run build:dev # bake dev as the default active
209
+ node bin/memoket.js doctor
210
+ node bin/memoket.js tools
211
+ node bin/memoket.js setup # registers into clients + browser login
212
+ node bin/memoket.js call get_memoket_overview
213
+ ```
214
+
215
+ Requires Node ≥ 18 (tested on Node 20+).
216
+
217
+ ## One honest caveat
218
+
219
+ **`memoket login`'s token is for this CLI's own `tools` / `call` only; it is not shared with Claude Code.** Claude Code does its own OAuth: after `memoket setup` registers the MCP, you still need to run `/mcp` in Claude Code and pick `memoket` to log in there. The CLI writes the config; each client does its own login. This is a client limitation, not something the CLI can route around.
220
+
221
+ ## Layout
222
+
223
+ ```
224
+ bin/memoket.js entry point
225
+ src/
226
+ cli.js commander command definitions
227
+ http-integration.js generic HTTP integration management commands
228
+ mcp-client.js JSON-RPC / SSE MCP client
229
+ oauth.js OAuth 2.0 + PKCE + DCR + callback + creds
230
+ register.js shell out to `claude mcp add` / `code --add-mcp`
231
+ config.js constants, version, credential path; imports config.local.js
232
+ config.local.js build-time-stamped endpoints table (gitignored)
233
+ utils.js tier / strip / coerce / short / or
234
+ config/
235
+ dev.json per-build-target endpoint table (build:dev)
236
+ test.json per-build-target endpoint table (build:test)
237
+ prod.json per-build-target endpoint table (build:prod)
238
+ scripts/
239
+ build.js node scripts/build.js <dev|test|prod>
240
+ ```
241
+
242
+ ## Release
243
+
244
+ `git tag v2.0.0 && git push origin v2.0.0` triggers `.github/workflows/release.yml`.
245
+ This is the **only** path that publishes to the public npm registry.
246
+
247
+ 1. Stamps `package.json` version
248
+ 2. `npm ci` + `node bin/memoket.js doctor` smoke test
249
+ 3. Creates a GitHub Release
250
+ 4. Runs `npm run build:prod` (so the published tarball bakes `prod` as the default endpoint)
251
+ 5. `npm publish --access public` (skipped if `NPM_TOKEN` secret is unset)
252
+
253
+ The `@memoket-ai` npm org must exist and a publish token must be set via `gh secret set NPM_TOKEN --repo memoket/memoket-cli`.
254
+
255
+ ### NPM_TOKEN management
256
+
257
+ **The `NPM_TOKEN` secret is the only credential with publish rights.** It must be a
258
+ [Granular Access Token](https://docs.npmjs.com/creating-and-viewing-access-tokens)
259
+ created at <https://www.npmjs.com/settings/memoket-ai/> → Access tokens, with:
260
+
261
+ | Setting | Value | Why |
262
+ |---|---|---|
263
+ | Packages | `@memoket-ai` (Read and Write) | Least privilege |
264
+ | Scope | Automation | CI/CD use only |
265
+ | Bypass 2FA | ✅ enabled | Required for headless `npm publish` |
266
+ | **IP allowlist** | `140.82.112.0/20`, `4.148.0.0/14` | **Pin to GitHub Actions public runner range** — this is what stops the token from being used locally even if leaked |
267
+ | Expiration | 90 days | Rotate via the npm UI; set a calendar reminder |
268
+
269
+ **Local publish is intentionally blocked on two layers:**
270
+ - The `publish` / `publish:prod` npm scripts have been removed from `package.json`; `node scripts/publish.js` exits with an error unless `CI=true`.
271
+ - The token itself is IP-restricted to GitHub Actions, so even with a stolen token, `npm publish` from a developer machine is rejected at the network layer.
272
+
273
+ The release workflow (`.github/workflows/release.yml`) and the manual dispatch
274
+ workflow (`.github/workflows/publish.yml`) are the **only** sanctioned publish paths.
275
+ Both read `secrets.NPM_TOKEN` at runtime — the value is never written to disk or
276
+ echoed in logs.
277
+
278
+ **Do not run `npm run publish:dev` / `npm run publish:test` against the public registry** — those scripts have been removed from `package.json`. The only local entry point is `node scripts/publish.js` (dry-run only, requires `CI=true` to actually publish). The release / dispatch workflows are the only sanctioned publish path.
package/bin/memoket.js ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ // Memoket CLI entry point. All command logic lives in src/; this file just
3
+ // wires up commander and translates unhandled errors into exit codes.
4
+
5
+ import { installProxy } from "../src/proxy.js";
6
+ import { buildProgram } from "../src/cli.js";
7
+
8
+ installProxy();
9
+
10
+ const program = buildProgram();
11
+ try {
12
+ await program.parseAsync(process.argv);
13
+ } catch (e) {
14
+ const cause = e.cause;
15
+ const causeMsg = cause && cause.code ? ` (${cause.code})` : "";
16
+ console.error("✗ " + (e.message || e) + causeMsg);
17
+ process.exit(1);
18
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "defaultEndpoints": {
3
+ "prod": "https://mcp.memoket.ai"
4
+ },
5
+ "defaultActiveEndpoint": "prod",
6
+ "apiBase": "https://api.memoket.ai"
7
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@memoket-ai/cli",
3
+ "version": "2.0.1",
4
+ "description": "Memoket MCP onboarding & passthrough CLI — register the Memoket MCP into your AI client and use it.",
5
+ "type": "module",
6
+ "bin": {
7
+ "memoket": "bin/memoket.js"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "src",
12
+ "config/prod.json",
13
+ "scripts",
14
+ "README.md",
15
+ "CHANGELOG.md",
16
+ "LICENSE"
17
+ ],
18
+ "scripts": {
19
+ "build": "node scripts/build.js",
20
+ "build:dev": "node scripts/build.js dev",
21
+ "build:test": "node scripts/build.js test",
22
+ "build:prod": "node scripts/build.js prod",
23
+ "pack": "npm run pack:prod",
24
+ "pack:dev": "npm run build:dev && npm pack --ignore-scripts",
25
+ "pack:test": "npm run build:test && npm pack --ignore-scripts",
26
+ "pack:prod": "npm run build:prod && npm pack --ignore-scripts",
27
+ "pretest": "npm run build:dev",
28
+ "test": "node bin/memoket.js doctor",
29
+ "prepack": "npm run build:prod"
30
+ },
31
+ "engines": {
32
+ "node": ">=18"
33
+ },
34
+ "dependencies": {
35
+ "commander": "^12.1.0",
36
+ "open": "^10.2.0",
37
+ "undici": "^6.0.0"
38
+ },
39
+ "keywords": [
40
+ "memoket",
41
+ "mcp",
42
+ "cli"
43
+ ],
44
+ "license": "SEE LICENSE IN LICENSE",
45
+ "publishConfig": {
46
+ "access": "public"
47
+ }
48
+ }
@@ -0,0 +1,100 @@
1
+ // build.js — pick a build target (dev|test|prod) and stamp src/config.local.js
2
+ // from config/<target>.json.
3
+ //
4
+ // node scripts/build.js [target] # target optional, interactive if missing
5
+ // npm run build # interactive: pick dev / test / prod
6
+ // npm run build:dev # explicit, no prompt
7
+ // npm run build:test
8
+ // npm run build:prod
9
+ //
10
+ // When the generated file is missing, src/config.js falls back to the last
11
+ // build's output; running `npm run build` once is required after a fresh
12
+ // checkout (prepack / pretest hooks do this automatically).
13
+
14
+ import { readFileSync, writeFileSync, existsSync } from "node:fs";
15
+ import { dirname, join } from "node:path";
16
+ import { fileURLToPath } from "node:url";
17
+ import { createInterface } from "node:readline";
18
+
19
+ const __dirname = dirname(fileURLToPath(import.meta.url));
20
+ const root = join(__dirname, "..");
21
+
22
+ const TARGETS = ["dev", "test", "prod"];
23
+
24
+ async function promptTarget() {
25
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
26
+ return new Promise((resolve) => {
27
+ console.log("Select build environment:");
28
+ for (const [i, t] of TARGETS.entries()) console.log(` ${i + 1}) ${t}`);
29
+ rl.question("> ", (answer) => {
30
+ rl.close();
31
+ const idx = parseInt(answer, 10) - 1;
32
+ const t = TARGETS[idx];
33
+ if (!t) {
34
+ console.error(`✗ invalid choice: ${answer}`);
35
+ process.exit(1);
36
+ }
37
+ resolve(t);
38
+ });
39
+ });
40
+ }
41
+
42
+ const arg = process.argv[2];
43
+ const target = (arg ? arg.toLowerCase() : await promptTarget());
44
+
45
+ if (!TARGETS.includes(target)) {
46
+ console.error(`✗ unknown build target: ${target}`);
47
+ console.error(` expected one of: ${TARGETS.join(", ")}`);
48
+ process.exit(1);
49
+ }
50
+
51
+ const srcPath = join(root, "config", `${target}.json`);
52
+ if (!existsSync(srcPath)) {
53
+ console.error(`✗ config file not found: ${srcPath}`);
54
+ process.exit(1);
55
+ }
56
+
57
+ const raw = readFileSync(srcPath, "utf8");
58
+ let parsed;
59
+ try {
60
+ parsed = JSON.parse(raw);
61
+ } catch (e) {
62
+ console.error(`✗ invalid JSON in ${srcPath}: ${e.message}`);
63
+ process.exit(1);
64
+ }
65
+
66
+ const { defaultEndpoints, defaultActiveEndpoint, apiBase } = parsed;
67
+ if (!defaultEndpoints || typeof defaultEndpoints !== "object") {
68
+ console.error(`✗ ${srcPath}: missing "defaultEndpoints" object`);
69
+ process.exit(1);
70
+ }
71
+ if (!defaultActiveEndpoint || !(defaultActiveEndpoint in defaultEndpoints)) {
72
+ console.error(
73
+ `✗ ${srcPath}: "defaultActiveEndpoint" must be one of ${Object.keys(defaultEndpoints).join(", ")}`,
74
+ );
75
+ process.exit(1);
76
+ }
77
+ if (!apiBase || typeof apiBase !== "string") {
78
+ console.error(`✗ ${srcPath}: missing "apiBase" string (the API base URL, e.g. "https://api.example.com")`);
79
+ process.exit(1);
80
+ }
81
+
82
+ const outPath = join(root, "src", "config.local.js");
83
+ const banner = `// AUTO-GENERATED by scripts/build.js — do not edit.
84
+ // Source: config/${target}.json
85
+ // Regenerate with: npm run build:${target}
86
+ `;
87
+
88
+ const body = `${banner}
89
+ export const defaultEndpoints = ${JSON.stringify(defaultEndpoints, null, 2)};
90
+
91
+ export const defaultActiveEndpoint = ${JSON.stringify(defaultActiveEndpoint)};
92
+
93
+ export const apiBase = ${JSON.stringify(apiBase)};
94
+ `;
95
+
96
+ writeFileSync(outPath, body, "utf8");
97
+ console.log(`✓ build target = ${target}`);
98
+ console.log(` config.local.js written (default active = ${defaultActiveEndpoint})`);
99
+ console.log(` endpoints: ${Object.keys(defaultEndpoints).join(", ")}`);
100
+ console.log(` apiBase: ${apiBase}`);