@hexis-ai/engram-server 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/memory-key-store.js +9 -11
- package/dist/adapters/memory.d.ts +8 -1
- package/dist/adapters/memory.js +0 -0
- package/dist/adapters/postgres-key-store.js +5 -11
- package/dist/adapters/postgres.d.ts +6 -1
- package/dist/adapters/postgres.js +83 -0
- package/dist/key-store.d.ts +29 -0
- package/dist/key-store.js +26 -0
- package/dist/migrations/0002-aliases.d.ts +2 -0
- package/dist/migrations/0002-aliases.js +30 -0
- package/dist/migrations/index.js +5 -1
- package/dist/openapi.js +213 -115
- package/dist/routes/persons.d.ts +8 -6
- package/dist/routes/persons.js +25 -7
- package/dist/routes/sessions.d.ts +5 -4
- package/dist/routes/sessions.js +12 -4
- package/dist/schemas.d.ts +5 -0
- package/dist/schemas.js +10 -0
- package/dist/server.js +5 -1
- package/dist/storage.d.ts +26 -1
- package/openapi.json +306 -116
- package/package.json +2 -2
package/dist/openapi.js
CHANGED
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
*
|
|
12
12
|
* `info.version` is the API version (the `/v1` surface), not the npm package
|
|
13
13
|
* version — it changes only when the wire contract changes.
|
|
14
|
+
*
|
|
15
|
+
* The human-readable strings (`summary`, `description`, response and
|
|
16
|
+
* parameter descriptions) are written in Japanese — the API reference is
|
|
17
|
+
* read by the Hexis team. Identifiers, paths, schema names and status
|
|
18
|
+
* codes stay as-is.
|
|
14
19
|
*/
|
|
15
20
|
import { z } from "zod";
|
|
16
21
|
import { createWorkspaceSchema, eventBatchSchema, issueKeySchema, personCreateSchema, personUpdateSchema, searchRequestSchema, sessionInitSchema, } from "./schemas";
|
|
@@ -37,212 +42,302 @@ const res = (description) => ({ description });
|
|
|
37
42
|
/** Default security: a workspace API key. `/admin/v1` paths override this. */
|
|
38
43
|
const workspaceAuth = [{ workspaceKey: [] }];
|
|
39
44
|
const adminAuth = [{ adminToken: [] }];
|
|
40
|
-
|
|
45
|
+
// ---------------------------------------------------------------------
|
|
46
|
+
// Tags — group the rendered reference (Scalar, on monet-web /docs/engram)
|
|
47
|
+
// into a per-domain sidebar instead of one flat list of 19 operations.
|
|
48
|
+
//
|
|
49
|
+
// The tag *definitions* (sidebar order + Japanese descriptions) live
|
|
50
|
+
// in TAG_DEFS; `tagged()` stamps a tag onto every operation in a
|
|
51
|
+
// path-item, so each path in buildPaths() declares its tag once,
|
|
52
|
+
// inline. test/openapi.test.ts guards that no operation slips through
|
|
53
|
+
// untagged and no operation references an undefined tag.
|
|
54
|
+
// ---------------------------------------------------------------------
|
|
55
|
+
/** Every tag, in sidebar order, with a Japanese description. */
|
|
56
|
+
const TAG_DEFS = [
|
|
57
|
+
{ name: "Me", description: "識別プローブ" },
|
|
58
|
+
{ name: "Sessions", description: "セッションの作成・取得・イベント追加" },
|
|
59
|
+
{ name: "Search", description: "ワークスペースコーパスへのスコアリング検索" },
|
|
60
|
+
{ name: "Persons", description: "person の作成・更新・検索" },
|
|
61
|
+
{
|
|
62
|
+
name: "Workspaces (admin)",
|
|
63
|
+
description: "ワークスペースの管理(管理者トークン必須)",
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "API Keys (admin)",
|
|
67
|
+
description: "ワークスペース API キーの発行・無効化(管理者トークン必須)",
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
/**
|
|
71
|
+
* Stamp `tag` onto every operation in a path-item. The path declares its
|
|
72
|
+
* tag once — `tagged("Sessions", { post: {…}, get: {…} })` — rather than
|
|
73
|
+
* repeating `tags` on each method.
|
|
74
|
+
*/
|
|
75
|
+
function tagged(tag, pathItem) {
|
|
76
|
+
const out = {};
|
|
77
|
+
for (const [method, op] of Object.entries(pathItem)) {
|
|
78
|
+
out[method] = { ...op, tags: [tag] };
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
const limitParam = queryParam("limit", "ページサイズ。サーバー側で上限が適用される。", {
|
|
41
83
|
type: "integer",
|
|
42
84
|
minimum: 1,
|
|
43
85
|
});
|
|
44
|
-
const channelParam = queryParam("channel", "
|
|
86
|
+
const channelParam = queryParam("channel", "セッションのチャンネルで絞り込む。");
|
|
45
87
|
function buildPaths() {
|
|
46
88
|
return {
|
|
47
|
-
"/v1/me": {
|
|
89
|
+
"/v1/me": tagged("Me", {
|
|
48
90
|
get: {
|
|
49
|
-
summary: "
|
|
50
|
-
responses: {
|
|
91
|
+
summary: "識別プローブ — 呼び出し元のキーが解決するワークスペースを返す。",
|
|
92
|
+
responses: {
|
|
93
|
+
"200": res("ワークスペース id"),
|
|
94
|
+
"401": res("認証エラー"),
|
|
95
|
+
},
|
|
51
96
|
},
|
|
52
|
-
},
|
|
53
|
-
"/v1/sessions": {
|
|
97
|
+
}),
|
|
98
|
+
"/v1/sessions": tagged("Sessions", {
|
|
54
99
|
post: {
|
|
55
|
-
summary: "
|
|
100
|
+
summary: "セッションを作成する。",
|
|
56
101
|
requestBody: jsonBody("SessionInit"),
|
|
57
102
|
responses: {
|
|
58
|
-
"200": res("
|
|
59
|
-
"400": res("
|
|
60
|
-
"401": res("
|
|
103
|
+
"200": res("作成されたセッション id"),
|
|
104
|
+
"400": res("リクエストボディが不正"),
|
|
105
|
+
"401": res("認証エラー"),
|
|
61
106
|
},
|
|
62
107
|
},
|
|
63
108
|
get: {
|
|
64
|
-
summary: "
|
|
109
|
+
summary: "最近のセッション一覧と、それに紐づく persons マップを取得する。",
|
|
65
110
|
parameters: [limitParam, channelParam],
|
|
66
|
-
responses: {
|
|
111
|
+
responses: {
|
|
112
|
+
"200": res("セッション一覧のエンベロープ"),
|
|
113
|
+
"401": res("認証エラー"),
|
|
114
|
+
},
|
|
67
115
|
},
|
|
68
|
-
},
|
|
69
|
-
"/v1/sessions/{id}": {
|
|
116
|
+
}),
|
|
117
|
+
"/v1/sessions/{id}": tagged("Sessions", {
|
|
70
118
|
get: {
|
|
71
|
-
summary: "
|
|
72
|
-
parameters: [pathParam("id", "
|
|
119
|
+
summary: "単一セッションと、それに紐づく persons マップを取得する。",
|
|
120
|
+
parameters: [pathParam("id", "セッション id。")],
|
|
73
121
|
responses: {
|
|
74
|
-
"200": res("
|
|
75
|
-
"404": res("
|
|
76
|
-
"401": res("
|
|
122
|
+
"200": res("セッションのエンベロープ"),
|
|
123
|
+
"404": res("セッションが見つからない"),
|
|
124
|
+
"401": res("認証エラー"),
|
|
77
125
|
},
|
|
78
126
|
},
|
|
79
|
-
},
|
|
80
|
-
"/v1/sessions/{id}/events": {
|
|
127
|
+
}),
|
|
128
|
+
"/v1/sessions/{id}/events": tagged("Sessions", {
|
|
81
129
|
post: {
|
|
82
|
-
summary: "
|
|
83
|
-
parameters: [pathParam("id", "
|
|
130
|
+
summary: "セッションにイベントを追加する。",
|
|
131
|
+
parameters: [pathParam("id", "セッション id。")],
|
|
84
132
|
requestBody: jsonBody("EventBatch"),
|
|
85
133
|
responses: {
|
|
86
|
-
"204": res("
|
|
87
|
-
"400": res("
|
|
88
|
-
"404": res("
|
|
89
|
-
"401": res("
|
|
134
|
+
"204": res("追加完了"),
|
|
135
|
+
"400": res("リクエストボディが不正"),
|
|
136
|
+
"404": res("セッションが見つからない"),
|
|
137
|
+
"401": res("認証エラー"),
|
|
90
138
|
},
|
|
91
139
|
},
|
|
92
|
-
|
|
93
|
-
|
|
140
|
+
get: {
|
|
141
|
+
summary: "セッションの生イベントログを seq 順で取得する(fold せず、各イベントの時刻つき)。",
|
|
142
|
+
parameters: [pathParam("id", "セッション id。")],
|
|
143
|
+
responses: {
|
|
144
|
+
"200": res("イベントログ"),
|
|
145
|
+
"404": res("セッションが見つからない"),
|
|
146
|
+
"401": res("認証エラー"),
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
}),
|
|
150
|
+
"/v1/search": tagged("Search", {
|
|
94
151
|
post: {
|
|
95
|
-
summary: "
|
|
152
|
+
summary: "クエリセッションに対してワークスペースのコーパスをスコアリングする。",
|
|
96
153
|
requestBody: jsonBody("SearchRequest"),
|
|
97
154
|
responses: {
|
|
98
|
-
"200": res("
|
|
99
|
-
"400": res("
|
|
100
|
-
"404": res("
|
|
101
|
-
"401": res("
|
|
155
|
+
"200": res("スコアリング結果と persons マップ"),
|
|
156
|
+
"400": res("リクエストボディが不正"),
|
|
157
|
+
"404": res("クエリセッションが見つからない"),
|
|
158
|
+
"401": res("認証エラー"),
|
|
102
159
|
},
|
|
103
160
|
},
|
|
104
|
-
},
|
|
105
|
-
"/v1/persons": {
|
|
161
|
+
}),
|
|
162
|
+
"/v1/persons": tagged("Persons", {
|
|
106
163
|
post: {
|
|
107
|
-
summary: "
|
|
164
|
+
summary: "person を作成する(id はサーバーが採番する)。",
|
|
108
165
|
requestBody: jsonBody("PersonCreate"),
|
|
109
166
|
responses: {
|
|
110
|
-
"201": res("
|
|
111
|
-
"400": res("
|
|
112
|
-
"401": res("
|
|
167
|
+
"201": res("作成された person"),
|
|
168
|
+
"400": res("リクエストボディが不正"),
|
|
169
|
+
"401": res("認証エラー"),
|
|
113
170
|
},
|
|
114
171
|
},
|
|
115
172
|
get: {
|
|
116
|
-
summary: "
|
|
117
|
-
parameters: [
|
|
118
|
-
|
|
173
|
+
summary: "person の一覧取得、またはフリーテキスト検索を行う。",
|
|
174
|
+
parameters: [
|
|
175
|
+
limitParam,
|
|
176
|
+
queryParam("q", "id と display_name に対するフリーテキストクエリ。"),
|
|
177
|
+
],
|
|
178
|
+
responses: {
|
|
179
|
+
"200": res("person 一覧"),
|
|
180
|
+
"401": res("認証エラー"),
|
|
181
|
+
},
|
|
119
182
|
},
|
|
120
|
-
},
|
|
121
|
-
"/v1/persons/{id}": {
|
|
183
|
+
}),
|
|
184
|
+
"/v1/persons/{id}": tagged("Persons", {
|
|
122
185
|
put: {
|
|
123
|
-
summary: "
|
|
124
|
-
parameters: [pathParam("id", "
|
|
186
|
+
summary: "呼び出し元が指定した id で person を upsert する。",
|
|
187
|
+
parameters: [pathParam("id", "person id。")],
|
|
125
188
|
requestBody: jsonBody("PersonCreate"),
|
|
126
189
|
responses: {
|
|
127
|
-
"200": res("
|
|
128
|
-
"400": res("
|
|
129
|
-
"401": res("
|
|
190
|
+
"200": res("upsert された person"),
|
|
191
|
+
"400": res("リクエストボディが不正"),
|
|
192
|
+
"401": res("認証エラー"),
|
|
130
193
|
},
|
|
131
194
|
},
|
|
132
195
|
patch: {
|
|
133
|
-
summary: "
|
|
134
|
-
parameters: [pathParam("id", "
|
|
196
|
+
summary: "person のプロフィール項目を部分更新する。",
|
|
197
|
+
parameters: [pathParam("id", "person id。")],
|
|
135
198
|
requestBody: jsonBody("PersonUpdate"),
|
|
136
199
|
responses: {
|
|
137
|
-
"200": res("
|
|
138
|
-
"400": res("
|
|
139
|
-
"404": res("person
|
|
140
|
-
"401": res("
|
|
200
|
+
"200": res("更新された person"),
|
|
201
|
+
"400": res("リクエストボディが不正"),
|
|
202
|
+
"404": res("person が見つからない"),
|
|
203
|
+
"401": res("認証エラー"),
|
|
141
204
|
},
|
|
142
205
|
},
|
|
143
206
|
get: {
|
|
144
|
-
summary: "
|
|
145
|
-
parameters: [pathParam("id", "
|
|
207
|
+
summary: "単一の person を取得する。",
|
|
208
|
+
parameters: [pathParam("id", "person id。")],
|
|
146
209
|
responses: {
|
|
147
210
|
"200": res("person"),
|
|
148
|
-
"404": res("person
|
|
149
|
-
"401": res("
|
|
211
|
+
"404": res("person が見つからない"),
|
|
212
|
+
"401": res("認証エラー"),
|
|
150
213
|
},
|
|
151
214
|
},
|
|
152
|
-
},
|
|
153
|
-
"/v1/persons/{id}/sessions": {
|
|
215
|
+
}),
|
|
216
|
+
"/v1/persons/{id}/sessions": tagged("Persons", {
|
|
154
217
|
get: {
|
|
155
|
-
summary: "
|
|
218
|
+
summary: "この person が参加している(または閲覧可能な)セッション一覧。",
|
|
156
219
|
parameters: [
|
|
157
|
-
pathParam("id", "
|
|
220
|
+
pathParam("id", "person id。"),
|
|
158
221
|
limitParam,
|
|
159
222
|
channelParam,
|
|
160
|
-
queryParam("scope", "`participant
|
|
223
|
+
queryParam("scope", "`participant`(デフォルト)または `viewable`。", {
|
|
161
224
|
type: "string",
|
|
162
225
|
enum: ["participant", "viewable"],
|
|
163
226
|
}),
|
|
164
227
|
],
|
|
165
|
-
responses: {
|
|
228
|
+
responses: {
|
|
229
|
+
"200": res("セッション一覧のエンベロープ"),
|
|
230
|
+
"401": res("認証エラー"),
|
|
231
|
+
},
|
|
166
232
|
},
|
|
167
|
-
},
|
|
168
|
-
"/
|
|
233
|
+
}),
|
|
234
|
+
"/v1/persons/{id}/aliases": tagged("Persons", {
|
|
235
|
+
get: {
|
|
236
|
+
summary: "この person の alias 一覧を取得する(直近使用が先頭)。",
|
|
237
|
+
parameters: [pathParam("id", "person id。")],
|
|
238
|
+
responses: {
|
|
239
|
+
"200": res("alias 一覧"),
|
|
240
|
+
"401": res("認証エラー"),
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
}),
|
|
244
|
+
"/v1/persons/{id}/aliases/{name}": tagged("Persons", {
|
|
245
|
+
put: {
|
|
246
|
+
summary: "person に alias を upsert する。name は case-insensitive で比較される。",
|
|
247
|
+
parameters: [
|
|
248
|
+
pathParam("id", "person id。"),
|
|
249
|
+
pathParam("name", "alias の名前(URL-encoded)。"),
|
|
250
|
+
],
|
|
251
|
+
requestBody: jsonBody("AliasUpsert"),
|
|
252
|
+
responses: {
|
|
253
|
+
"200": res("upsert された alias"),
|
|
254
|
+
"400": res("リクエストボディが不正"),
|
|
255
|
+
"404": res("person が見つからない"),
|
|
256
|
+
"401": res("認証エラー"),
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
}),
|
|
260
|
+
"/admin/v1/workspaces": tagged("Workspaces (admin)", {
|
|
169
261
|
post: {
|
|
170
|
-
summary: "
|
|
262
|
+
summary: "ワークスペースを作成する(デフォルトで初期キーも発行する)。",
|
|
171
263
|
security: adminAuth,
|
|
172
264
|
requestBody: jsonBody("CreateWorkspace"),
|
|
173
265
|
responses: {
|
|
174
|
-
"200": res("
|
|
175
|
-
"400": res("
|
|
176
|
-
"401": res("
|
|
266
|
+
"200": res("ワークスペース(issueKey=false でない限りキーも含む)"),
|
|
267
|
+
"400": res("リクエストボディまたはワークスペース id が不正"),
|
|
268
|
+
"401": res("認証エラー"),
|
|
177
269
|
},
|
|
178
270
|
},
|
|
179
271
|
get: {
|
|
180
|
-
summary: "
|
|
272
|
+
summary: "全ワークスペースを一覧取得する。",
|
|
181
273
|
security: adminAuth,
|
|
182
|
-
responses: {
|
|
274
|
+
responses: {
|
|
275
|
+
"200": res("ワークスペース一覧"),
|
|
276
|
+
"401": res("認証エラー"),
|
|
277
|
+
},
|
|
183
278
|
},
|
|
184
|
-
},
|
|
185
|
-
"/admin/v1/workspaces/{id}": {
|
|
279
|
+
}),
|
|
280
|
+
"/admin/v1/workspaces/{id}": tagged("Workspaces (admin)", {
|
|
186
281
|
get: {
|
|
187
|
-
summary: "
|
|
282
|
+
summary: "単一のワークスペースを取得する。",
|
|
188
283
|
security: adminAuth,
|
|
189
|
-
parameters: [pathParam("id", "
|
|
284
|
+
parameters: [pathParam("id", "ワークスペース id。")],
|
|
190
285
|
responses: {
|
|
191
|
-
"200": res("
|
|
192
|
-
"404": res("
|
|
193
|
-
"401": res("
|
|
286
|
+
"200": res("ワークスペース"),
|
|
287
|
+
"404": res("ワークスペースが見つからない"),
|
|
288
|
+
"401": res("認証エラー"),
|
|
194
289
|
},
|
|
195
290
|
},
|
|
196
291
|
delete: {
|
|
197
|
-
summary: "
|
|
292
|
+
summary: "ワークスペースを削除する(キー・セッション・イベントにカスケードする)。",
|
|
198
293
|
security: adminAuth,
|
|
199
|
-
parameters: [pathParam("id", "
|
|
294
|
+
parameters: [pathParam("id", "ワークスペース id。")],
|
|
200
295
|
responses: {
|
|
201
|
-
"204": res("
|
|
202
|
-
"404": res("
|
|
203
|
-
"401": res("
|
|
296
|
+
"204": res("削除完了"),
|
|
297
|
+
"404": res("ワークスペースが見つからない"),
|
|
298
|
+
"401": res("認証エラー"),
|
|
204
299
|
},
|
|
205
300
|
},
|
|
206
|
-
},
|
|
207
|
-
"/admin/v1/workspaces/{id}/keys": {
|
|
301
|
+
}),
|
|
302
|
+
"/admin/v1/workspaces/{id}/keys": tagged("API Keys (admin)", {
|
|
208
303
|
post: {
|
|
209
|
-
summary: "
|
|
304
|
+
summary: "ワークスペースに新しい API キーを発行する。",
|
|
210
305
|
security: adminAuth,
|
|
211
|
-
parameters: [pathParam("id", "
|
|
306
|
+
parameters: [pathParam("id", "ワークスペース id。")],
|
|
212
307
|
requestBody: jsonBody("IssueKey", false),
|
|
213
308
|
responses: {
|
|
214
|
-
"200": res("
|
|
215
|
-
"400": res("
|
|
216
|
-
"404": res("
|
|
217
|
-
"401": res("
|
|
309
|
+
"200": res("発行されたキー(生のキーは一度のみ返却)"),
|
|
310
|
+
"400": res("リクエストボディが不正"),
|
|
311
|
+
"404": res("ワークスペースが見つからない"),
|
|
312
|
+
"401": res("認証エラー"),
|
|
218
313
|
},
|
|
219
314
|
},
|
|
220
315
|
get: {
|
|
221
|
-
summary: "
|
|
316
|
+
summary: "ワークスペースの API キー一覧を取得する(ハッシュのみ)。",
|
|
222
317
|
security: adminAuth,
|
|
223
|
-
parameters: [pathParam("id", "
|
|
318
|
+
parameters: [pathParam("id", "ワークスペース id。")],
|
|
224
319
|
responses: {
|
|
225
|
-
"200": res("
|
|
226
|
-
"404": res("
|
|
227
|
-
"401": res("
|
|
320
|
+
"200": res("キー一覧"),
|
|
321
|
+
"404": res("ワークスペースが見つからない"),
|
|
322
|
+
"401": res("認証エラー"),
|
|
228
323
|
},
|
|
229
324
|
},
|
|
230
|
-
},
|
|
231
|
-
"/admin/v1/workspaces/{id}/keys/{keyId}": {
|
|
325
|
+
}),
|
|
326
|
+
"/admin/v1/workspaces/{id}/keys/{keyId}": tagged("API Keys (admin)", {
|
|
232
327
|
delete: {
|
|
233
|
-
summary: "
|
|
328
|
+
summary: "API キーを無効化する。",
|
|
234
329
|
security: adminAuth,
|
|
235
330
|
parameters: [
|
|
236
|
-
pathParam("id", "
|
|
237
|
-
pathParam("keyId", "
|
|
331
|
+
pathParam("id", "ワークスペース id。"),
|
|
332
|
+
pathParam("keyId", "キー id。"),
|
|
238
333
|
],
|
|
239
334
|
responses: {
|
|
240
|
-
"204": res("
|
|
241
|
-
"404": res("
|
|
242
|
-
"401": res("
|
|
335
|
+
"204": res("無効化完了(冪等)"),
|
|
336
|
+
"404": res("キーが見つからない"),
|
|
337
|
+
"401": res("認証エラー"),
|
|
243
338
|
},
|
|
244
339
|
},
|
|
245
|
-
},
|
|
340
|
+
}),
|
|
246
341
|
};
|
|
247
342
|
}
|
|
248
343
|
/**
|
|
@@ -256,12 +351,14 @@ export function buildOpenApiDocument() {
|
|
|
256
351
|
info: {
|
|
257
352
|
title: "engram-server",
|
|
258
353
|
version: "1.0",
|
|
259
|
-
description: "
|
|
260
|
-
"
|
|
261
|
-
"
|
|
354
|
+
description: "AI エージェント向けのクロスセッション検索。リクエストボディは" +
|
|
355
|
+
"サーバーの Zod スキーマ(src/schemas.ts)から導出される。" +
|
|
356
|
+
"レスポンススキーマは現状ステータスコードのみの記述で、" +
|
|
357
|
+
"本格的な記述は今後対応予定。",
|
|
262
358
|
},
|
|
359
|
+
tags: TAG_DEFS.map((t) => ({ ...t })),
|
|
263
360
|
servers: [
|
|
264
|
-
{ url: "/", description: "
|
|
361
|
+
{ url: "/", description: "デプロイされた engram-server からの相対パス" },
|
|
265
362
|
],
|
|
266
363
|
security: workspaceAuth,
|
|
267
364
|
components: {
|
|
@@ -269,13 +366,14 @@ export function buildOpenApiDocument() {
|
|
|
269
366
|
workspaceKey: {
|
|
270
367
|
type: "http",
|
|
271
368
|
scheme: "bearer",
|
|
272
|
-
description: "
|
|
369
|
+
description: "ワークスペース API キー(`eng_…`)。`X-Api-Key` ヘッダーでも受け付ける。",
|
|
273
370
|
},
|
|
274
371
|
adminToken: {
|
|
275
372
|
type: "apiKey",
|
|
276
373
|
in: "header",
|
|
277
374
|
name: "X-Admin-Token",
|
|
278
|
-
description: "
|
|
375
|
+
description: "`/admin/v1/*` 用のプラットフォーム管理者トークン。" +
|
|
376
|
+
"`Authorization: Bearer` でも受け付ける。",
|
|
279
377
|
},
|
|
280
378
|
},
|
|
281
379
|
schemas: {
|
package/dist/routes/persons.d.ts
CHANGED
|
@@ -3,11 +3,13 @@ import type { Env } from "../context";
|
|
|
3
3
|
import { type RouteConfig } from "./helpers";
|
|
4
4
|
/**
|
|
5
5
|
* Person routes. Mount under `/v1`:
|
|
6
|
-
* POST /v1/persons
|
|
7
|
-
* PUT /v1/persons/:id
|
|
8
|
-
* PATCH /v1/persons/:id
|
|
9
|
-
* GET /v1/persons/:id
|
|
10
|
-
* GET /v1/persons
|
|
11
|
-
* GET /v1/persons/:id/sessions
|
|
6
|
+
* POST /v1/persons create a person (server allocates the id)
|
|
7
|
+
* PUT /v1/persons/:id upsert at a host-supplied id
|
|
8
|
+
* PATCH /v1/persons/:id patch profile fields
|
|
9
|
+
* GET /v1/persons/:id fetch one person
|
|
10
|
+
* GET /v1/persons list / free-text search persons
|
|
11
|
+
* GET /v1/persons/:id/sessions sessions this person participates in / can view
|
|
12
|
+
* PUT /v1/persons/:id/aliases/:name upsert an alias for this person
|
|
13
|
+
* GET /v1/persons/:id/aliases list aliases for this person (newest-used-first)
|
|
12
14
|
*/
|
|
13
15
|
export declare function personsRoutes(cfg: RouteConfig): Hono<Env>;
|
package/dist/routes/persons.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import { parseJsonBody, personCreateSchema, personUpdateSchema } from "../schemas";
|
|
2
|
+
import { aliasUpsertSchema, parseJsonBody, personCreateSchema, personUpdateSchema, } from "../schemas";
|
|
3
3
|
import { clampLimit, resolvePersonMap } from "./helpers";
|
|
4
4
|
/**
|
|
5
5
|
* Person routes. Mount under `/v1`:
|
|
6
|
-
* POST /v1/persons
|
|
7
|
-
* PUT /v1/persons/:id
|
|
8
|
-
* PATCH /v1/persons/:id
|
|
9
|
-
* GET /v1/persons/:id
|
|
10
|
-
* GET /v1/persons
|
|
11
|
-
* GET /v1/persons/:id/sessions
|
|
6
|
+
* POST /v1/persons create a person (server allocates the id)
|
|
7
|
+
* PUT /v1/persons/:id upsert at a host-supplied id
|
|
8
|
+
* PATCH /v1/persons/:id patch profile fields
|
|
9
|
+
* GET /v1/persons/:id fetch one person
|
|
10
|
+
* GET /v1/persons list / free-text search persons
|
|
11
|
+
* GET /v1/persons/:id/sessions sessions this person participates in / can view
|
|
12
|
+
* PUT /v1/persons/:id/aliases/:name upsert an alias for this person
|
|
13
|
+
* GET /v1/persons/:id/aliases list aliases for this person (newest-used-first)
|
|
12
14
|
*/
|
|
13
15
|
export function personsRoutes(cfg) {
|
|
14
16
|
const app = new Hono();
|
|
@@ -50,6 +52,22 @@ export function personsRoutes(cfg) {
|
|
|
50
52
|
const persons = await c.var.ctx.storage.listPersons({ limit, q });
|
|
51
53
|
return c.json({ persons });
|
|
52
54
|
});
|
|
55
|
+
app.put("/persons/:id/aliases/:name", async (c) => {
|
|
56
|
+
const id = c.req.param("id");
|
|
57
|
+
const name = c.req.param("name");
|
|
58
|
+
const body = await parseJsonBody(c, aliasUpsertSchema);
|
|
59
|
+
if (body instanceof Response)
|
|
60
|
+
return body;
|
|
61
|
+
const alias = await c.var.ctx.storage.upsertAlias(id, { name, ...body });
|
|
62
|
+
if (!alias)
|
|
63
|
+
return c.json({ error: "person_not_found" }, 404);
|
|
64
|
+
return c.json(alias);
|
|
65
|
+
});
|
|
66
|
+
app.get("/persons/:id/aliases", async (c) => {
|
|
67
|
+
const id = c.req.param("id");
|
|
68
|
+
const aliases = await c.var.ctx.storage.listAliases(id);
|
|
69
|
+
return c.json({ aliases });
|
|
70
|
+
});
|
|
53
71
|
app.get("/persons/:id/sessions", async (c) => {
|
|
54
72
|
const id = c.req.param("id");
|
|
55
73
|
const limit = clampLimit(c, cfg.defaultListLimit, cfg.maxListLimit);
|
|
@@ -3,9 +3,10 @@ import type { Env } from "../context";
|
|
|
3
3
|
import { type RouteConfig } from "./helpers";
|
|
4
4
|
/**
|
|
5
5
|
* Session routes. Mount under `/v1`:
|
|
6
|
-
* POST /v1/sessions
|
|
7
|
-
* POST /v1/sessions/:id/events
|
|
8
|
-
* GET /v1/sessions/:id
|
|
9
|
-
* GET /v1/sessions
|
|
6
|
+
* POST /v1/sessions create a session
|
|
7
|
+
* POST /v1/sessions/:id/events append events
|
|
8
|
+
* GET /v1/sessions/:id fetch one session + its persons map
|
|
9
|
+
* GET /v1/sessions/:id/events fetch the raw, ordered event log
|
|
10
|
+
* GET /v1/sessions list recent sessions + persons map
|
|
10
11
|
*/
|
|
11
12
|
export declare function sessionsRoutes(cfg: RouteConfig): Hono<Env>;
|
package/dist/routes/sessions.js
CHANGED
|
@@ -3,10 +3,11 @@ import { eventBatchSchema, parseJsonBody, sessionInitSchema } from "../schemas";
|
|
|
3
3
|
import { clampLimit, resolvePersonMap } from "./helpers";
|
|
4
4
|
/**
|
|
5
5
|
* Session routes. Mount under `/v1`:
|
|
6
|
-
* POST /v1/sessions
|
|
7
|
-
* POST /v1/sessions/:id/events
|
|
8
|
-
* GET /v1/sessions/:id
|
|
9
|
-
* GET /v1/sessions
|
|
6
|
+
* POST /v1/sessions create a session
|
|
7
|
+
* POST /v1/sessions/:id/events append events
|
|
8
|
+
* GET /v1/sessions/:id fetch one session + its persons map
|
|
9
|
+
* GET /v1/sessions/:id/events fetch the raw, ordered event log
|
|
10
|
+
* GET /v1/sessions list recent sessions + persons map
|
|
10
11
|
*/
|
|
11
12
|
export function sessionsRoutes(cfg) {
|
|
12
13
|
const app = new Hono();
|
|
@@ -40,6 +41,13 @@ export function sessionsRoutes(cfg) {
|
|
|
40
41
|
const persons = await resolvePersonMap(c.var.ctx.storage, [s]);
|
|
41
42
|
return c.json({ session: s, persons });
|
|
42
43
|
});
|
|
44
|
+
app.get("/sessions/:id/events", async (c) => {
|
|
45
|
+
const id = c.req.param("id");
|
|
46
|
+
const events = await c.var.ctx.storage.getSessionEvents(id);
|
|
47
|
+
if (events === null)
|
|
48
|
+
return c.json({ error: "session_not_found" }, 404);
|
|
49
|
+
return c.json({ events });
|
|
50
|
+
});
|
|
43
51
|
app.get("/sessions", async (c) => {
|
|
44
52
|
const limit = clampLimit(c, cfg.defaultListLimit, cfg.maxListLimit);
|
|
45
53
|
const channel = c.req.query("channel") || undefined;
|
package/dist/schemas.d.ts
CHANGED
|
@@ -66,6 +66,11 @@ export declare const personCreateSchema: z.ZodObject<{
|
|
|
66
66
|
export declare const personUpdateSchema: z.ZodObject<{
|
|
67
67
|
display_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
68
68
|
}, z.core.$strip>;
|
|
69
|
+
export declare const aliasUpsertSchema: z.ZodObject<{
|
|
70
|
+
caller: z.ZodString;
|
|
71
|
+
last_used: z.ZodString;
|
|
72
|
+
increment: z.ZodOptional<z.ZodBoolean>;
|
|
73
|
+
}, z.core.$strip>;
|
|
69
74
|
export declare const searchRequestSchema: z.ZodObject<{
|
|
70
75
|
query: z.ZodUnion<readonly [z.ZodObject<{
|
|
71
76
|
sessionId: z.ZodString;
|
package/dist/schemas.js
CHANGED
|
@@ -56,6 +56,16 @@ export const personCreateSchema = z.object({
|
|
|
56
56
|
export const personUpdateSchema = z.object({
|
|
57
57
|
display_name: z.string().nullable().optional(),
|
|
58
58
|
});
|
|
59
|
+
// --- Aliases ----------------------------------------------------------
|
|
60
|
+
export const aliasUpsertSchema = z.object({
|
|
61
|
+
caller: z.string().min(1),
|
|
62
|
+
// YYYY-MM-DD; loose-validate so callers can also pass an ISO timestamp
|
|
63
|
+
// and the server will accept the date prefix.
|
|
64
|
+
last_used: z
|
|
65
|
+
.string()
|
|
66
|
+
.regex(/^\d{4}-\d{2}-\d{2}/, "expected YYYY-MM-DD"),
|
|
67
|
+
increment: z.boolean().optional(),
|
|
68
|
+
});
|
|
59
69
|
// --- Search ----------------------------------------------------------
|
|
60
70
|
const searchQuerySchema = z.union([
|
|
61
71
|
z.object({ sessionId: z.string().min(1) }),
|
package/dist/server.js
CHANGED
|
@@ -53,10 +53,13 @@ export function createServer(opts) {
|
|
|
53
53
|
sessions: "POST/GET /v1/sessions",
|
|
54
54
|
sessionById: "GET /v1/sessions/:id",
|
|
55
55
|
events: "POST /v1/sessions/:id/events",
|
|
56
|
+
sessionEvents: "GET /v1/sessions/:id/events",
|
|
56
57
|
search: "POST /v1/search",
|
|
57
58
|
persons: "POST/GET /v1/persons",
|
|
58
59
|
personById: "GET/PUT/PATCH /v1/persons/:id",
|
|
59
60
|
personSessions: "GET /v1/persons/:id/sessions",
|
|
61
|
+
personAliases: "GET /v1/persons/:id/aliases",
|
|
62
|
+
upsertAlias: "PUT /v1/persons/:id/aliases/:name",
|
|
60
63
|
},
|
|
61
64
|
}));
|
|
62
65
|
app.get("/healthz", (c) => c.json({ ok: true }));
|
|
@@ -76,7 +79,8 @@ export function createServer(opts) {
|
|
|
76
79
|
await next();
|
|
77
80
|
});
|
|
78
81
|
// Identity probe — echoes the workspace the caller's key resolves to.
|
|
79
|
-
// Used by clients (e.g. engram
|
|
82
|
+
// Used by host clients (e.g. monet's `/v1/engram/*` proxy) to label
|
|
83
|
+
// which tenant they're viewing.
|
|
80
84
|
app.get("/v1/me", (c) => c.json({ workspaceId: c.var.ctx.workspaceId }));
|
|
81
85
|
app.route("/v1", sessionsRoutes(cfg));
|
|
82
86
|
app.route("/v1", personsRoutes(cfg));
|