@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 +77 -0
- package/LICENSE +65 -0
- package/README.md +278 -0
- package/bin/memoket.js +18 -0
- package/config/prod.json +7 -0
- package/package.json +48 -0
- package/scripts/build.js +100 -0
- package/scripts/publish.js +227 -0
- package/src/auth-basic.js +65 -0
- package/src/cli.js +475 -0
- package/src/config.js +77 -0
- package/src/config.local.js +11 -0
- package/src/endpoints.js +112 -0
- package/src/gateway.js +128 -0
- package/src/http-integration.js +155 -0
- package/src/mcp-client.js +134 -0
- package/src/oauth.js +291 -0
- package/src/proxy.js +40 -0
- package/src/register.js +43 -0
- package/src/utils.js +83 -0
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
|
+
}
|
package/config/prod.json
ADDED
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
|
+
}
|
package/scripts/build.js
ADDED
|
@@ -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}`);
|