@oneentry/mcp-platform-server 0.1.1 → 0.1.3

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.
Files changed (120) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +62 -61
  3. package/dist/api/audit.d.ts +0 -11
  4. package/dist/api/audit.js +0 -10
  5. package/dist/api/auth.d.ts +0 -18
  6. package/dist/api/auth.js +0 -20
  7. package/dist/api/build-catalog.d.ts +0 -14
  8. package/dist/api/build-catalog.js +7 -77
  9. package/dist/api/catalog.d.ts +0 -26
  10. package/dist/api/catalog.js +3 -30
  11. package/dist/api/client.d.ts +0 -24
  12. package/dist/api/client.js +6 -28
  13. package/dist/api/normalize-schema.d.ts +0 -11
  14. package/dist/api/normalize-schema.js +0 -18
  15. package/dist/api/policy.d.ts +0 -23
  16. package/dist/api/policy.js +0 -0
  17. package/dist/api/shape.d.ts +0 -13
  18. package/dist/api/shape.js +0 -21
  19. package/dist/api/swagger-source.d.ts +1 -12
  20. package/dist/api/swagger-source.js +2 -20
  21. package/dist/api/types.d.ts +0 -34
  22. package/dist/api/types.js +0 -1
  23. package/dist/bin/cli.d.ts +0 -1
  24. package/dist/bin/cli.js +1 -5
  25. package/dist/config/config.d.ts +0 -54
  26. package/dist/config/config.js +0 -60
  27. package/dist/index.d.ts +0 -5
  28. package/dist/index.js +0 -5
  29. package/dist/knowledge/chunk.d.ts +0 -15
  30. package/dist/knowledge/chunk.js +0 -16
  31. package/dist/knowledge/github.d.ts +0 -25
  32. package/dist/knowledge/github.js +0 -40
  33. package/dist/knowledge/loader.d.ts +0 -28
  34. package/dist/knowledge/loader.js +0 -26
  35. package/dist/knowledge/search.d.ts +0 -22
  36. package/dist/knowledge/search.js +0 -34
  37. package/dist/knowledge/tar.d.ts +0 -15
  38. package/dist/knowledge/tar.js +0 -33
  39. package/dist/knowledge/types.d.ts +0 -35
  40. package/dist/knowledge/types.js +0 -1
  41. package/dist/server.d.ts +0 -12
  42. package/dist/server.js +10 -14
  43. package/dist/session.d.ts +0 -23
  44. package/dist/session.js +0 -18
  45. package/dist/tools/api-call.d.ts +0 -2
  46. package/dist/tools/api-call.js +0 -23
  47. package/dist/tools/api-discovery.d.ts +0 -2
  48. package/dist/tools/api-discovery.js +2 -5
  49. package/dist/tools/docs.d.ts +0 -2
  50. package/dist/tools/docs.js +1 -5
  51. package/dist/tools/guide.d.ts +0 -7
  52. package/dist/tools/guide.js +2 -9
  53. package/dist/tools/result.d.ts +0 -8
  54. package/dist/tools/result.js +0 -7
  55. package/dist/tools/whoami.d.ts +0 -2
  56. package/dist/tools/whoami.js +1 -3
  57. package/dist/transports/http.d.ts +0 -6
  58. package/dist/transports/http.js +0 -26
  59. package/dist/transports/stdio.d.ts +0 -6
  60. package/dist/transports/stdio.js +0 -6
  61. package/knowledge/operating-rules.md +70 -125
  62. package/package.json +18 -5
  63. package/dist/api/audit.d.ts.map +0 -1
  64. package/dist/api/audit.js.map +0 -1
  65. package/dist/api/auth.d.ts.map +0 -1
  66. package/dist/api/auth.js.map +0 -1
  67. package/dist/api/build-catalog.d.ts.map +0 -1
  68. package/dist/api/build-catalog.js.map +0 -1
  69. package/dist/api/catalog.d.ts.map +0 -1
  70. package/dist/api/catalog.js.map +0 -1
  71. package/dist/api/client.d.ts.map +0 -1
  72. package/dist/api/client.js.map +0 -1
  73. package/dist/api/normalize-schema.d.ts.map +0 -1
  74. package/dist/api/normalize-schema.js.map +0 -1
  75. package/dist/api/policy.d.ts.map +0 -1
  76. package/dist/api/policy.js.map +0 -1
  77. package/dist/api/shape.d.ts.map +0 -1
  78. package/dist/api/shape.js.map +0 -1
  79. package/dist/api/swagger-source.d.ts.map +0 -1
  80. package/dist/api/swagger-source.js.map +0 -1
  81. package/dist/api/types.d.ts.map +0 -1
  82. package/dist/api/types.js.map +0 -1
  83. package/dist/bin/cli.d.ts.map +0 -1
  84. package/dist/bin/cli.js.map +0 -1
  85. package/dist/config/config.d.ts.map +0 -1
  86. package/dist/config/config.js.map +0 -1
  87. package/dist/index.d.ts.map +0 -1
  88. package/dist/index.js.map +0 -1
  89. package/dist/knowledge/chunk.d.ts.map +0 -1
  90. package/dist/knowledge/chunk.js.map +0 -1
  91. package/dist/knowledge/github.d.ts.map +0 -1
  92. package/dist/knowledge/github.js.map +0 -1
  93. package/dist/knowledge/loader.d.ts.map +0 -1
  94. package/dist/knowledge/loader.js.map +0 -1
  95. package/dist/knowledge/search.d.ts.map +0 -1
  96. package/dist/knowledge/search.js.map +0 -1
  97. package/dist/knowledge/tar.d.ts.map +0 -1
  98. package/dist/knowledge/tar.js.map +0 -1
  99. package/dist/knowledge/types.d.ts.map +0 -1
  100. package/dist/knowledge/types.js.map +0 -1
  101. package/dist/server.d.ts.map +0 -1
  102. package/dist/server.js.map +0 -1
  103. package/dist/session.d.ts.map +0 -1
  104. package/dist/session.js.map +0 -1
  105. package/dist/tools/api-call.d.ts.map +0 -1
  106. package/dist/tools/api-call.js.map +0 -1
  107. package/dist/tools/api-discovery.d.ts.map +0 -1
  108. package/dist/tools/api-discovery.js.map +0 -1
  109. package/dist/tools/docs.d.ts.map +0 -1
  110. package/dist/tools/docs.js.map +0 -1
  111. package/dist/tools/guide.d.ts.map +0 -1
  112. package/dist/tools/guide.js.map +0 -1
  113. package/dist/tools/result.d.ts.map +0 -1
  114. package/dist/tools/result.js.map +0 -1
  115. package/dist/tools/whoami.d.ts.map +0 -1
  116. package/dist/tools/whoami.js.map +0 -1
  117. package/dist/transports/http.d.ts.map +0 -1
  118. package/dist/transports/http.js.map +0 -1
  119. package/dist/transports/stdio.d.ts.map +0 -1
  120. package/dist/transports/stdio.js.map +0 -1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OneEntry
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @oneentry/mcp-platform-server
2
2
 
3
- MCP server that lets an AI agent **operate the OneEntry Admin API** — grounded in the project's own
4
- rules rather than in guesswork.
3
+ MCP server that lets an AI agent **operate the OneEntry Admin API** — grounded in published
4
+ operating rules rather than in guesswork.
5
5
 
6
6
  Nothing is frozen into the package. Both halves are fetched at runtime and cached:
7
7
 
@@ -9,14 +9,15 @@ Nothing is frozen into the package. Both halves are fetched at runtime and cache
9
9
  ([ONEENTRY-PLATFORM/oneentry-platform-rules](https://github.com/ONEENTRY-PLATFORM/oneentry-platform-rules)),
10
10
  chunked into searchable sections. Editing a doc there — through the GitHub UI, a PR, whatever — takes effect on the next
11
11
  restart of any server, with no npm release.
12
- - **Actuation.** Every Admin API operation, built from the stand's own
12
+ - **Actuation.** Every Admin API operation, built from the instance's own API document at
13
13
  `GET /api/admin/workflows/nodes/admin-api/swagger.json`, with parameter and body schemas, the
14
14
  permission each one requires, and a risk classification — reachable through one generic invoke
15
15
  tool instead of one tool per endpoint. The catalog always describes the API that is actually
16
16
  running, not a snapshot of it.
17
17
 
18
- The one thing still committed here is `data/permissions.json`: `@GrantByPermission` is a decorator
19
- and never reaches any swagger, so that map has to be recovered from the CMS sources.
18
+ The one thing still committed here is `data/permissions.json`: the permission an operation requires
19
+ is declared in platform code and never reaches the API document, so that map has to be recovered
20
+ separately.
20
21
 
21
22
  Only the Admin API is exposed.
22
23
 
@@ -26,7 +27,7 @@ Only the Admin API is exposed.
26
27
  npm i -g @oneentry/mcp-platform-server # or use npx, below
27
28
  ```
28
29
 
29
- ## Local mode (agent and CMS on the same machine)
30
+ ## Local mode (agent and instance on the same machine)
30
31
 
31
32
  Add to `.mcp.json` in the repo you work from:
32
33
 
@@ -46,21 +47,21 @@ Add to `.mcp.json` in the repo you work from:
46
47
  }
47
48
  ```
48
49
 
49
- No checkout of the CMS is needed. On first run the knowledge repository is downloaded once and
50
+ Nothing has to be checked out locally. On first run the knowledge repository is downloaded once and
50
51
  cached; later runs re-use the cache and only ask GitHub whether the commit moved, at most once an
51
52
  hour.
52
53
 
53
- backend-app must be running with `API_TYPE=/api/admin` (default port 3003).
54
+ The base URL must point at an instance that exposes the Admin API under `/api/admin`.
54
55
 
55
- ## Remote mode (hosted, many agents, one stand)
56
+ ## Remote mode (hosted, many agents, one instance)
56
57
 
57
58
  ```bash
58
59
  oneentry-mcp-platform --http --port 8931 --audit /var/log/oneentry-mcp-platform-audit.jsonl \
59
- --base-url https://stand.example/api/admin \
60
+ --base-url https://your-instance.example/api/admin \
60
61
  --allowed-origins https://agent.example
61
62
  ```
62
63
 
63
- Each MCP session carries its own CMS identity, passed as connection headers — never as tool
64
+ Each MCP session carries its own identity, passed as connection headers — never as tool
64
65
  arguments, so a prompt-injected instruction cannot swap identities:
65
66
 
66
67
  | header | meaning |
@@ -78,7 +79,7 @@ Sessions never share tokens or confirm tokens. `GET /health` reports liveness an
78
79
  | `cms_docs_search` | search the knowledge base, returns sections with snippets |
79
80
  | `cms_docs_read` | read one section, with its sibling sections listed for paging |
80
81
  | `cms_api_search` | find operations by keyword / tag / method / mutating |
81
- | `cms_api_describe` | params, body schema, required permission, risk, linked docs |
82
+ | `cms_api_describe` | params, body schema, required permission, risk, confirm gating |
82
83
  | `cms_api_call` | execute one operation; `dryRun` and confirm-gating for mutations |
83
84
  | `cms_whoami` | mode, base URL, admin + permissions, knowledge commit, catalog state and warnings |
84
85
 
@@ -100,8 +101,8 @@ Read-only by default. `--allow` (or `ONEENTRY_MCP_ALLOW`) raises it:
100
101
  `auth/logout/all-users`), require a **confirm token**: the first call returns the token *and the
101
102
  current state of the target*, fetched via the sibling GET, so a human can see what is about to
102
103
  change. The token is bound to `hash(opId + arguments)`, single-use, valid 5 minutes.
103
- - Permissions are checked locally first: if the admin lacks the `AdminPermissionsEnum` value the
104
- operation declares, the call is refused without being sent.
104
+ - Permissions are checked locally first: if the admin lacks the permission the operation declares,
105
+ the call is refused without being sent.
105
106
  - Every non-GET call is appended to a JSONL audit log (arguments are hashed, not stored). Required
106
107
  in remote mode.
107
108
 
@@ -111,10 +112,10 @@ A public GitHub repository of small markdown files. Layout:
111
112
 
112
113
  ```
113
114
  knowledge/
114
- mcp/operating-rules.md -> docId "mcp/operating-rules"
115
- back/docs/menus-module.md -> docId "back/docs/menus-module"
116
- back/docs/orders-module/index.md -> docId "back/docs/orders-module"
117
- back/docs/orders-module/status-axis.md-> docId "back/docs/orders-module/status-axis"
115
+ mcp/operating-rules.md -> docId "mcp/operating-rules"
116
+ mcp/docs/api/orders.md -> docId "mcp/docs/api/orders"
117
+ mcp/docs/server/cms-api-call.md -> docId "mcp/docs/server/cms-api-call"
118
+ mcp/docs/api/orders/index.md -> docId "mcp/docs/api/orders"
118
119
  ```
119
120
 
120
121
  The docId is the path under `knowledge/` without `.md`; `<name>/index.md` collapses back to
@@ -140,10 +141,10 @@ restart is instant and the previous commit is pruned.
140
141
  Which repository and which commit is a configuration choice, not a constant: `--knowledge-repo`
141
142
  (`ONEENTRY_MCP_KNOWLEDGE_REPO`, default `ONEENTRY-PLATFORM/oneentry-platform-rules`) and
142
143
  `--knowledge-ref`
143
- (`ONEENTRY_MCP_KNOWLEDGE_REF`, default `main`) — a ref may be a branch, a tag or a commit sha, so a
144
- stand can be frozen on a known-good corpus. `ONEENTRY_GITHUB_TOKEN` is optional and only raises the
145
- API rate limit; the repo is public. `cms_whoami` reports the repo, ref and 12-character commit the
146
- running server actually loaded.
144
+ (`ONEENTRY_MCP_KNOWLEDGE_REF`, default `main`) — a ref may be a branch, a tag or a commit sha, so an
145
+ instance can be frozen on a known-good corpus. `ONEENTRY_GITHUB_TOKEN` is optional and only raises
146
+ the API rate limit; the repo is public. `cms_whoami` reports the repo, ref and 12-character commit
147
+ the running server actually loaded.
147
148
 
148
149
  ### Reading the docs from a local folder instead
149
150
 
@@ -165,67 +166,67 @@ look like an empty corpus.
165
166
 
166
167
  ### Known limitation: retrieval is single-language
167
168
 
168
- Search does not translate. The backend corpus is largely English, so a Russian question will not
169
- find an English-only document no matter how it is weighted and the reverse holds too. The fix is
170
- content, not code: give such a document a short summary section in the other language, or ask in the
171
- language the document is written in.
169
+ Search does not translate, and the corpus is English. A question asked in another language will not
170
+ find an English-only document no matter how it is weighted. The fix is content, not code.
172
171
 
173
- ### Publishing CMS docs into it
172
+ ### The corpus is hand-authored
174
173
 
175
- The knowledge repo is what the server reads; the CMS repos remain where the docs are written.
176
- To move them across:
174
+ The knowledge repository is written for a public audience, one file at a time. There is no import
175
+ script and one must not be added: bulk-copying internal documentation into it would publish source
176
+ paths, storage identifiers and tracker ids in a single command, and a repository that is public and
177
+ mirrored cannot be un-published. `npm run publish:knowledge` exists only to refuse.
177
178
 
178
- ```bash
179
- npm run publish:knowledge -- --back ../dnk-back --front ../dnk-front --out ../oneentry-platform-rules
180
- ```
181
-
182
- It copies `.claude/docs`, `.claude/tests` and `.claude/rules` from both checkouts, splits anything
183
- over 12 KB at `##` boundaries, and reports files in the target it did not produce. It **does not
184
- commit or push** — that diff is the moment internal text becomes public, and a human owns it.
185
- `.claude/fixes` and the CMS `CLAUDE.md` files are deliberately not published.
179
+ At release time, copy `knowledge/mcp/operating-rules.md` from the rules repository over this
180
+ package's `knowledge/operating-rules.md` the rules repository is the source, and the bundled seed
181
+ is never hand-edited.
186
182
 
187
183
  ## Where the catalog comes from
188
184
 
189
185
  Two sources, because neither alone is enough:
190
186
 
191
187
  1. `GET {baseUrl}/workflows/nodes/admin-api/swagger.json?langCode=en_US` — paths, operation ids and
192
- schemas, straight from the stand you are pointed at. Cached per base URL, so an unreachable
193
- stand still yields the last known catalog.
194
- 2. `data/permissions.json` — the `@GrantByPermission` decorators, which never reach swagger.
195
- Regenerate it when the CMS changes its permissions:
188
+ schemas, straight from the instance you are pointed at. Cached per base URL, so an unreachable
189
+ instance still yields the last known catalog.
190
+ 2. `data/permissions.json` — the permission each operation requires, which the API document does not
191
+ express. Regenerate it when the platform changes its permissions:
196
192
 
197
193
  ```bash
198
- npm run sync:permissions -- --back ../dnk-back
194
+ npm run sync:permissions -- --back <path to a platform checkout>
199
195
  ```
200
196
 
201
- That pass walks base classes too, since Nest derives the operation id from the *registered*
202
- controller while the handler often lives in a parent.
197
+ That pass walks base classes too, since the operation id is derived from the *registered* class
198
+ while the handler often lives in a parent.
203
199
 
204
200
  Inconsistencies are surfaced as catalog `warnings` (visible in `cms_whoami`) rather than swallowed:
205
201
  paths outside `/api/admin` (dropped — this server exposes the Admin API only), operations with no
206
- `operationId`, handlers whose permission is known but which the stand does not expose, and
207
- permissions required by controllers but absent from `AdminPermissionsEnum`no admin can hold
208
- those, so such endpoints answer 403 forever.
202
+ `operationId`, operations whose permission is known but which the instance does not expose, and
203
+ permissions that operations require but no admin can hold such endpoints answer 403 forever.
209
204
 
210
- If the stand does not serve its swagger at all, the catalog is **empty** and says so loudly. Empty
211
- is safe — there is nothing to call — but silence would read as "the CMS has no such endpoint".
205
+ If the instance does not serve its API document at all, the catalog is **empty** and says so loudly.
206
+ Empty is safe — there is nothing to call — but silence would read as "the platform has no such
207
+ endpoint".
212
208
 
213
- ### The swagger is not valid JSON Schema
209
+ ### The API document is not valid JSON Schema
214
210
 
215
- Many `type` values in the source swagger are TypeScript expressions (`Record<string, any>`,
216
- `CommonLocalizeInfos`, `AttributesSets`, `image`, `date`). The build normalizes what it can and
217
- marks the rest `"x-loose": true` with the original under `x-source-type`. For loose fields the
218
- `example` is the contract, and client-side body validation is deliberately advisory — the server
219
- is the real validator, so a call is never blocked because a loose field could not be checked.
211
+ Many `type` values in the source document are expressions in the platform's own type language
212
+ (`Record<string, any>`, `CommonLocalizeInfos`, `AttributesSets`, `image`, `date`). The build
213
+ normalizes what it can and marks the rest `"x-loose": true` with the original under `x-source-type`.
214
+ For loose fields the `example` is the contract, and client-side body validation is deliberately
215
+ advisory — the instance is the real validator, so a call is never blocked because a loose field
216
+ could not be checked.
220
217
 
221
218
  ## Tests
222
219
 
223
220
  ```bash
224
- npm test # unit + MCP protocol + remote-mode transport (no stand needed)
225
- npm run test:integration # against a live dnk-back; skips cleanly if unreachable
221
+ npm test # unit + MCP protocol + remote-mode transport (no instance needed)
222
+ npm run test:integration # against a live instance; skips cleanly if unreachable
226
223
  ```
227
224
 
228
- The integration suite needs backend-app on `:3003` with `API_TYPE=/api/admin` plus
229
- `ONEENTRY_CMS_LOGIN`/`ONEENTRY_CMS_PASSWORD`. It walks search → describe → call, asserts a read-only refusal
230
- sends nothing, and runs a full create → confirm-gated delete cycle on a throwaway template while
231
- checking the audit log.
225
+ The integration suite needs `ONEENTRY_CMS_BASE_URL` pointing at an instance that exposes the Admin
226
+ API, plus `ONEENTRY_CMS_LOGIN`/`ONEENTRY_CMS_PASSWORD`. It walks search → describe → call, asserts a
227
+ read-only refusal sends nothing, and runs a full create → confirm-gated delete cycle on a throwaway
228
+ template while checking the audit log.
229
+
230
+ ## License
231
+
232
+ MIT — see [LICENSE](LICENSE).
@@ -1,4 +1,3 @@
1
- /** Одна запись аудита изменяющего вызова. */
2
1
  export interface AuditEntry {
3
2
  at: string;
4
3
  mode: string;
@@ -10,11 +9,6 @@ export interface AuditEntry {
10
9
  outcome: 'denied' | 'needs-confirm' | 'sent';
11
10
  status?: number;
12
11
  }
13
- /**
14
- * Журнал изменяющих вызовов в формате JSONL.
15
- * Пишем хэш аргументов, а не сами аргументы: тела запросов содержат
16
- * пользовательские данные, а для разбора инцидента достаточно связать вызовы между собой.
17
- */
18
12
  export declare class AuditLog {
19
13
  private readonly path;
20
14
  private readonly mode;
@@ -23,10 +17,5 @@ export declare class AuditLog {
23
17
  mode: string;
24
18
  });
25
19
  static hashArgs(args: unknown): string;
26
- /**
27
- * Пишет запись, не роняя вызов при недоступности файла: аудит важен,
28
- * но потеря строки лога — не причина провалить операцию, о которой уже отчитались.
29
- */
30
20
  record(entry: Omit<AuditEntry, 'at' | 'mode'>): void;
31
21
  }
32
- //# sourceMappingURL=audit.d.ts.map
package/dist/api/audit.js CHANGED
@@ -1,10 +1,5 @@
1
1
  import { appendFileSync } from 'node:fs';
2
2
  import { createHash } from 'node:crypto';
3
- /**
4
- * Журнал изменяющих вызовов в формате JSONL.
5
- * Пишем хэш аргументов, а не сами аргументы: тела запросов содержат
6
- * пользовательские данные, а для разбора инцидента достаточно связать вызовы между собой.
7
- */
8
3
  export class AuditLog {
9
4
  path;
10
5
  mode;
@@ -15,10 +10,6 @@ export class AuditLog {
15
10
  static hashArgs(args) {
16
11
  return createHash('sha256').update(JSON.stringify(args ?? null)).digest('hex').slice(0, 16);
17
12
  }
18
- /**
19
- * Пишет запись, не роняя вызов при недоступности файла: аудит важен,
20
- * но потеря строки лога — не причина провалить операцию, о которой уже отчитались.
21
- */
22
13
  record(entry) {
23
14
  if (!this.path) {
24
15
  return;
@@ -32,4 +23,3 @@ export class AuditLog {
32
23
  }
33
24
  }
34
25
  }
35
- //# sourceMappingURL=audit.js.map
@@ -1,31 +1,21 @@
1
1
  import type { Config } from '../config/config.js';
2
- /** Пара токенов Admin API, как её отдаёт `POST /auth/login`. */
3
2
  export interface TokenPair {
4
3
  accessToken: string;
5
4
  refreshToken: string;
6
5
  }
7
- /** Данные аутентифицированного админа, восстановленные из токена и API. */
8
6
  export interface AdminIdentity {
9
7
  id: number;
10
8
  login?: string;
11
9
  permissions: string[];
12
10
  }
13
- /** Учётные данные одной сессии. В remote-режиме у каждой сессии свои. */
14
11
  export interface Credentials {
15
12
  login?: string;
16
13
  password?: string;
17
14
  token?: string;
18
15
  }
19
- /** Ошибка аутентификации с текстом, пригодным для показа модели. */
20
16
  export declare class AuthError extends Error {
21
17
  }
22
- /** Достаёт идентификатор админа из payload access-токена. */
23
18
  export declare const adminIdFromToken: (token: string) => number;
24
- /**
25
- * Держит пару токенов для одного набора учётных данных.
26
- * Токены живут только в памяти и только внутри сессии: в remote-режиме
27
- * это то, что не даёт токену одной сессии утечь в другую.
28
- */
29
19
  export declare class TokenStore {
30
20
  private pair;
31
21
  private readonly baseUrl;
@@ -36,18 +26,10 @@ export declare class TokenStore {
36
26
  timeoutMs: number;
37
27
  credentials: Credentials;
38
28
  });
39
- /** Есть ли чем аутентифицироваться вообще. */
40
29
  get isConfigured(): boolean;
41
30
  private post;
42
31
  private static readPair;
43
- /** Возвращает access-токен, логинясь при первом обращении. */
44
32
  accessToken(): Promise<string>;
45
- /**
46
- * Обновляет пару после 401. Если refresh невозможен (передан готовый токен
47
- * или refresh отвергнут) — пробует полный логин, и только потом сдаётся.
48
- */
49
33
  refresh(): Promise<string>;
50
34
  }
51
- /** Учётные данные из конфигурации — источник по умолчанию для local-режима. */
52
35
  export declare const credentialsFromConfig: (config: Config) => Credentials;
53
- //# sourceMappingURL=auth.d.ts.map
package/dist/api/auth.js CHANGED
@@ -1,4 +1,3 @@
1
- /** Ошибка аутентификации с текстом, пригодным для показа модели. */
2
1
  export class AuthError extends Error {
3
2
  }
4
3
  const decodeJwtPayload = (token) => {
@@ -13,7 +12,6 @@ const decodeJwtPayload = (token) => {
13
12
  }
14
13
  return parsed;
15
14
  };
16
- /** Достаёт идентификатор админа из payload access-токена. */
17
15
  export const adminIdFromToken = (token) => {
18
16
  const payload = decodeJwtPayload(token);
19
17
  const id = payload['id'] ?? payload['sub'] ?? payload['userId'];
@@ -23,11 +21,6 @@ export const adminIdFromToken = (token) => {
23
21
  }
24
22
  return numeric;
25
23
  };
26
- /**
27
- * Держит пару токенов для одного набора учётных данных.
28
- * Токены живут только в памяти и только внутри сессии: в remote-режиме
29
- * это то, что не даёт токену одной сессии утечь в другую.
30
- */
31
24
  export class TokenStore {
32
25
  pair;
33
26
  baseUrl;
@@ -41,7 +34,6 @@ export class TokenStore {
41
34
  this.pair = { accessToken: params.credentials.token, refreshToken: '' };
42
35
  }
43
36
  }
44
- /** Есть ли чем аутентифицироваться вообще. */
45
37
  get isConfigured() {
46
38
  return Boolean(this.credentials.token || (this.credentials.login && this.credentials.password));
47
39
  }
@@ -54,11 +46,6 @@ export class TokenStore {
54
46
  });
55
47
  const text = await response.text();
56
48
  if (!response.ok) {
57
- /**
58
- * Admin API отвечает 401 «Login or password incorrect» и на developer-аккаунт:
59
- * контроллер отбрасывает админов с isDeveloper. Без этой подсказки агент
60
- * будет бесконечно перепроверять пароль.
61
- */
62
49
  const hint = response.status === 401
63
50
  ? ' The Admin API also returns 401 for accounts flagged isDeveloper — those belong to the Developer API, which this server does not expose.'
64
51
  : '';
@@ -81,7 +68,6 @@ export class TokenStore {
81
68
  refreshToken: typeof refreshToken === 'string' ? refreshToken : '',
82
69
  };
83
70
  }
84
- /** Возвращает access-токен, логинясь при первом обращении. */
85
71
  async accessToken() {
86
72
  if (this.pair) {
87
73
  return this.pair.accessToken;
@@ -95,10 +81,6 @@ export class TokenStore {
95
81
  }));
96
82
  return this.pair.accessToken;
97
83
  }
98
- /**
99
- * Обновляет пару после 401. Если refresh невозможен (передан готовый токен
100
- * или refresh отвергнут) — пробует полный логин, и только потом сдаётся.
101
- */
102
84
  async refresh() {
103
85
  const refreshToken = this.pair?.refreshToken;
104
86
  this.pair = undefined;
@@ -117,10 +99,8 @@ export class TokenStore {
117
99
  return this.accessToken();
118
100
  }
119
101
  }
120
- /** Учётные данные из конфигурации — источник по умолчанию для local-режима. */
121
102
  export const credentialsFromConfig = (config) => ({
122
103
  ...(config.login ? { login: config.login } : {}),
123
104
  ...(config.password ? { password: config.password } : {}),
124
105
  ...(config.token ? { token: config.token } : {}),
125
106
  });
126
- //# sourceMappingURL=auth.js.map
@@ -1,24 +1,10 @@
1
1
  import type { Catalog } from './types.js';
2
- /**
3
- * Связь операция -> требуемое право и полный список прав.
4
- * Собирается на сборке проходом по исходникам dnk-back: декоратор
5
- * `@GrantByPermission` в swagger не попадает никогда, ни в файловый, ни в живой.
6
- */
7
2
  export interface PermissionMap {
8
3
  byOpId: Record<string, string>;
9
4
  permissions: string[];
10
5
  }
11
- /**
12
- * Строит каталог операций из swagger стенда и карты прав.
13
- * Два источника, потому что ни одного не хватает: swagger даёт пути и схемы,
14
- * а требуемое право объявлено декоратором и в swagger не попадает.
15
- * Классификация риска и список навсегда подтверждаемых путей вычисляются здесь,
16
- * то есть в коде пакета, — swagger приходит по сети и не должен на них влиять.
17
- */
18
6
  export declare const buildCatalog: (params: {
19
- /** Тело `swagger.json` как строка: по нему же считается хэш. */
20
7
  rawSwagger: string;
21
8
  permissions: PermissionMap;
22
9
  builtAt: string;
23
10
  }) => Catalog;
24
- //# sourceMappingURL=build-catalog.d.ts.map
@@ -2,11 +2,6 @@ import { createHash } from 'node:crypto';
2
2
  import { capSchema, normalizeSchema } from './normalize-schema.js';
3
3
  const API_PREFIX = '/api/admin';
4
4
  const METHODS = ['get', 'post', 'put', 'patch', 'delete'];
5
- /**
6
- * Операции, которые остаются за подтверждением при любом уровне `--allow`.
7
- * Список путей, а не прав: право можно выдать админу по недоразумению,
8
- * а этот барьер снять нельзя.
9
- */
10
5
  const ALWAYS_CONFIRM = [
11
6
  /^\/immutable-settings/,
12
7
  /^\/admins/,
@@ -17,42 +12,6 @@ const ALWAYS_CONFIRM = [
17
12
  /^\/settings-general/,
18
13
  /^\/system\/captcha-keys/,
19
14
  ];
20
- /** Какие разделы документации объясняют операции конкретного тега. */
21
- const TAG_DOCS = {
22
- Orders: ['back/docs/orders-module', 'back/docs/order-status-axis'],
23
- Products: [
24
- 'back/docs/postgres-db-with-attribute-index',
25
- 'back/docs/product-relations',
26
- 'back/docs/isPrice-attribute',
27
- ],
28
- Payments: ['back/docs/payments-module', 'back/docs/isPrice-attribute'],
29
- Blocks: ['back/docs/blocks-module'],
30
- Pages: ['back/docs/pages-module'],
31
- Discounts: ['back/docs/orders-module'],
32
- Filters: ['back/docs/filters'],
33
- 'Attributes Sets': [
34
- 'back/docs/postgres-db-with-attribute-index',
35
- 'back/docs/attribute-indexing',
36
- 'back/tests/attributes-sets-e2e',
37
- ],
38
- Menus: ['back/docs/menus-module', 'back/tests/menus-e2e'],
39
- 'Index Attributes': ['back/docs/attribute-indexing'],
40
- Modules: ['back/docs/user-permissions'],
41
- Subscriptions: ['back/docs/subscriptions-module'],
42
- Forms: ['back/docs/forms-and-form-data'],
43
- FormData: ['back/docs/forms-and-form-data'],
44
- 'User Permissions': ['back/docs/user-permissions', 'front/docs/user-permissions'],
45
- 'User Groups': ['back/docs/user-permissions'],
46
- Admins: ['back/docs/user-permissions', 'back/tests/admins-e2e'],
47
- 'Global Search': ['back/docs/global-search'],
48
- 'AI Gateway': ['back/docs/ai-gateway'],
49
- 'AI Gateway / settings': ['back/docs/ai-gateway'],
50
- 'File Uploading': ['back/docs/file-upload-dev-mode'],
51
- System: ['back/docs/captcha-recaptcha'],
52
- Templates: ['back/tests/templates-e2e'],
53
- 'Template previews': ['back/tests/template-previews-e2e'],
54
- Import: ['back/tests/import-e2e'],
55
- };
56
15
  const riskOf = (method) => {
57
16
  if (method === 'get') {
58
17
  return 'read';
@@ -61,7 +20,6 @@ const riskOf = (method) => {
61
20
  };
62
21
  const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
63
22
  const asString = (value) => typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined;
64
- /** Собирает одну человекочитаемую строку описания успешного ответа. */
65
23
  const responseSummary = (responses) => {
66
24
  if (!isRecord(responses)) {
67
25
  return undefined;
@@ -124,13 +82,6 @@ const buildBody = (raw, components) => {
124
82
  required: raw['required'] === true,
125
83
  };
126
84
  };
127
- /**
128
- * Строит каталог операций из swagger стенда и карты прав.
129
- * Два источника, потому что ни одного не хватает: swagger даёт пути и схемы,
130
- * а требуемое право объявлено декоратором и в swagger не попадает.
131
- * Классификация риска и список навсегда подтверждаемых путей вычисляются здесь,
132
- * то есть в коде пакета, — swagger приходит по сети и не должен на них влиять.
133
- */
134
85
  export const buildCatalog = (params) => {
135
86
  const { rawSwagger } = params;
136
87
  const swagger = JSON.parse(rawSwagger);
@@ -143,19 +94,12 @@ export const buildCatalog = (params) => {
143
94
  : {};
144
95
  const { byOpId, permissions } = params.permissions;
145
96
  const operations = [];
146
- /** Пути не из Admin API и операции без operationId — оба случая идут в warnings. */
147
97
  const foreignPaths = [];
148
98
  const withoutOpId = [];
149
99
  for (const [fullPath, pathItem] of Object.entries(swagger['paths'])) {
150
100
  if (!isRecord(pathItem)) {
151
101
  continue;
152
102
  }
153
- /**
154
- * Путь вне `/api/admin` в каталог не попадает никогда: сервер по замыслу
155
- * не выпускает наружу Content и Developer API. Документ приходит по сети,
156
- * поэтому здесь фильтр, а не исключение — одна лишняя запись не должна
157
- * лишать агента всего каталога.
158
- */
159
103
  if (!fullPath.startsWith(API_PREFIX)) {
160
104
  foreignPaths.push(fullPath);
161
105
  continue;
@@ -194,7 +138,6 @@ export const buildCatalog = (params) => {
194
138
  ...(responseSummary(operation['responses'])
195
139
  ? { responseSummary: responseSummary(operation['responses']) }
196
140
  : {}),
197
- docLinks: [...(TAG_DOCS[tag] ?? []), 'mcp/operating-rules'],
198
141
  searchText: '',
199
142
  };
200
143
  entry.searchText = [opId, method.toUpperCase(), path, tag, summary, permission ?? '']
@@ -213,26 +156,14 @@ export const buildCatalog = (params) => {
213
156
  warnings.push(`${String(withoutOpId.length)} operation(s) have no operationId and cannot be called ` +
214
157
  `(e.g. ${withoutOpId.slice(0, 3).join(', ')}). Report it against the CMS.`);
215
158
  }
216
- /**
217
- * Обработчик объявляет право, но живой swagger о нём не знает.
218
- * Раньше это означало устаревший снимок swagger; теперь документ приходит
219
- * со стенда, поэтому смысл другой: карта прав собрана с другой версии кода,
220
- * чем развёрнута на стенде. Агенту это важно так же — иначе он получит 404
221
- * на существующий, по его данным, эндпоинт.
222
- */
223
159
  const known = new Set(operations.map((o) => o.opId));
224
160
  const missing = Object.keys(byOpId).filter((opId) => !known.has(opId) && !opId.startsWith('Developer') && !opId.startsWith('Content'));
225
161
  if (missing.length > 0) {
226
- warnings.push(`${String(missing.length)} admin handler(s) declare a permission but are absent from the ` +
227
- `stand's swagger (e.g. ${missing.slice(0, 3).join(', ')}). data/permissions.json was ` +
228
- 'generated from a different revision of dnk-back than the stand runs — re-run ' +
229
- 'npm run sync:permissions if the catalog looks incomplete.');
162
+ warnings.push(`${String(missing.length)} admin operation(s) declare a permission but are absent from the ` +
163
+ `instance's API document (e.g. ${missing.slice(0, 3).join(', ')}). The bundled permission ` +
164
+ 'map was generated from a different platform revision than this instance runs — update ' +
165
+ 'the package if the catalog looks incomplete.');
230
166
  }
231
- /**
232
- * Право, объявленное контроллером, но отсутствующее в `AdminPermissionsEnum`,
233
- * никогда не будет выдано администратору — такой эндпоинт отвечает 403 всегда.
234
- * Это дефект dnk-back, и о нём должен знать и агент, и человек.
235
- */
236
167
  const knownPermissions = new Set(permissions);
237
168
  const orphanPermissions = [
238
169
  ...new Set(operations
@@ -240,9 +171,9 @@ export const buildCatalog = (params) => {
240
171
  .filter((value) => value !== undefined && !knownPermissions.has(value))),
241
172
  ];
242
173
  if (orphanPermissions.length > 0) {
243
- warnings.push(`${orphanPermissions.length} permission(s) are required by controllers but missing from ` +
244
- `AdminPermissionsEnum (${orphanPermissions.join(', ')}). No admin can hold them, so those ` +
245
- 'operations always answer 403 report it rather than retrying.');
174
+ warnings.push(`${orphanPermissions.length} permission(s) are required by operations but cannot be held by ` +
175
+ `any admin (${orphanPermissions.join(', ')}). Those operations answer 403 permanently ` +
176
+ 'treat them as unavailable and report it rather than retrying.');
246
177
  }
247
178
  return {
248
179
  version: 1,
@@ -254,4 +185,3 @@ export const buildCatalog = (params) => {
254
185
  operations,
255
186
  };
256
187
  };
257
- //# sourceMappingURL=build-catalog.js.map