@runlot/api 0.1.0-rc.39 → 0.1.0-rc.40

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/schema.d.ts CHANGED
@@ -1399,10 +1399,14 @@ export interface paths {
1399
1399
  get: operations["listMembers"];
1400
1400
  put?: never;
1401
1401
  /**
1402
- * 기존 사용자를 멤버로 넣는다 (`runlot org add-member`)
1403
- * @description **초대가 아니다** (docs/local-mvp.md §3 초대 UI 제외). 사용자는 이미
1404
- * 한 번 로그인해 `users` 있어야 하고, 없으면 404 `user_not_found`.
1405
- * 이미 멤버면 409 `already_member` — 역할을 바꾸는 것은 PUT 이다.
1402
+ * 기존 사용자를 멤버로 넣는다 (운영·자동화용)
1403
+ * @description **초대가 아니다.** 사용자는 이미 로그인해 `users` 있어야 하고
1404
+ * (`provider`+`subject` 가리킨다), 없으면 404 `user_not_found`. 이미
1405
+ * 멤버면 409 `already_member` — 역할을 바꾸는 것은 PUT 이다.
1406
+ *
1407
+ * 사람을 부르는 표면은 이것이 아니라 `POST …/invites` 다
1408
+ * (docs/members.md): GitHub 의 `subject` 는 숫자 id 라 관리자가 알 수
1409
+ * 없는 값이고, 이 끝점은 그것을 이미 아는 자동화의 것이다.
1406
1410
  *
1407
1411
  * admin 이상. 감사 `member.add`.
1408
1412
  */
@@ -1413,6 +1417,133 @@ export interface paths {
1413
1417
  patch?: never;
1414
1418
  trace?: never;
1415
1419
  };
1420
+ "/v1/orgs/{orgSlug}/invites": {
1421
+ parameters: {
1422
+ query?: never;
1423
+ header?: never;
1424
+ path: {
1425
+ orgSlug: components["parameters"]["OrgSlug"];
1426
+ };
1427
+ cookie?: never;
1428
+ };
1429
+ /**
1430
+ * 대기 중인 초대 (`runlot member invites`)
1431
+ * @description **admin 이상.** 멤버 목록은 member 이상이 보지만 초대 목록은 다르다 —
1432
+ * 아직 조직에 없는 사람들의 이메일이고, 그것은 명부가 아니라 관리 작업의
1433
+ * 상태다.
1434
+ *
1435
+ * 수락·폐기된 초대는 안 나온다 (이력의 자리는 감사 기록이다). 만료된
1436
+ * 초대는 나온다 — 거기서 관리자가 할 일이 재발송이다.
1437
+ */
1438
+ get: operations["listInvites"];
1439
+ put?: never;
1440
+ /**
1441
+ * 이메일로 초대한다 (`runlot member invite`)
1442
+ * @description admin 이상. 메일은 즉시 나가지 않는다 — cp-core 의 발송 회차가 큐를
1443
+ * 훑는다 (`sentAt` 이 비어 있으면 아직 큐에 있다는 뜻이다).
1444
+ *
1445
+ * **같은 주소로 다시 부르면 재발송이다**: 새 초대가 아니라 같은 행이
1446
+ * 갱신되고 (토큰·만료가 새로 서고 역할이 바뀐다) 200 으로 답한다. 새
1447
+ * 초대는 201 이다.
1448
+ *
1449
+ * 이미 멤버인 주소는 409 `already_member`. 대기 중 초대가 너무 많으면
1450
+ * 409 `too_many_invites` — 요금제 한도가 아니라 발신 남용 방지다.
1451
+ *
1452
+ * 수명은 이레, 한 번 쓰면 끝, 언제든 폐기할 수 있다. 감사 `member.invite`.
1453
+ */
1454
+ post: operations["createInvite"];
1455
+ delete?: never;
1456
+ options?: never;
1457
+ head?: never;
1458
+ patch?: never;
1459
+ trace?: never;
1460
+ };
1461
+ "/v1/orgs/{orgSlug}/invites/{inviteId}": {
1462
+ parameters: {
1463
+ query?: never;
1464
+ header?: never;
1465
+ path: {
1466
+ orgSlug: components["parameters"]["OrgSlug"];
1467
+ inviteId: string;
1468
+ };
1469
+ cookie?: never;
1470
+ };
1471
+ get?: never;
1472
+ put?: never;
1473
+ post?: never;
1474
+ /**
1475
+ * 초대를 취소한다 (`runlot member uninvite`)
1476
+ * @description admin 이상. 아직 안 나간 메일은 **안 나간다** — 폐기가 발송 큐도 비운다.
1477
+ * 이미 수락·폐기된 초대는 404. 감사 `member.uninvite`.
1478
+ */
1479
+ delete: operations["revokeInvite"];
1480
+ options?: never;
1481
+ head?: never;
1482
+ patch?: never;
1483
+ trace?: never;
1484
+ };
1485
+ "/v1/invites/{token}": {
1486
+ parameters: {
1487
+ query?: never;
1488
+ header?: never;
1489
+ path: {
1490
+ token: string;
1491
+ };
1492
+ cookie?: never;
1493
+ };
1494
+ /**
1495
+ * 초대 미리보기 (인증 없음)
1496
+ * @description **인증이 없다.** 초대 링크를 받은 사람은 아직 로그인하지 않았고, 로그인
1497
+ * 하기 전에 이것이 무엇인지 알아야 한다. 토큰은 32 바이트 난수라 열거할
1498
+ * 수 없고, 그래서 여는 대가가 "우리가 메일을 보낸 사람이 조직 이름을
1499
+ * 본다" 로 끝난다.
1500
+ *
1501
+ * **상태는 오류가 아니라 값이다** (`state`). 만료된 초대에 404 를 주면
1502
+ * 화면이 "링크가 틀렸다" 와 "링크가 늙었다" 를 구분하지 못한다.
1503
+ *
1504
+ * 모르는 토큰은 404 — 오래전에 폐기된 토큰과 같은 답이다.
1505
+ */
1506
+ get: operations["getInvite"];
1507
+ put?: never;
1508
+ post?: never;
1509
+ delete?: never;
1510
+ options?: never;
1511
+ head?: never;
1512
+ patch?: never;
1513
+ trace?: never;
1514
+ };
1515
+ "/v1/invites/{token}/accept": {
1516
+ parameters: {
1517
+ query?: never;
1518
+ header?: never;
1519
+ path: {
1520
+ token: string;
1521
+ };
1522
+ cookie?: never;
1523
+ };
1524
+ get?: never;
1525
+ put?: never;
1526
+ /**
1527
+ * 초대를 수락한다
1528
+ * @description 로그인이 필요하다. **로그인한 계정의 이메일이 초대한 주소와 같아야
1529
+ * 한다는 규칙은 없다** — GitHub 비공개 이메일로 로그인한 사람은
1530
+ * `users.email` 이 아예 없고, 그 규칙을 넣으면 그 사람은 어떤 조직에도
1531
+ * 들어올 수 없다. 대신 수명 이레 · 한 번 쓰면 끝 · 언제든 폐기이고,
1532
+ * 감사 기록에 초대한 주소와 수락한 계정이 **둘 다** 남는다.
1533
+ *
1534
+ * **이미 멤버면 역할이 안 바뀐다** (`alreadyMember: true`). 초대장 하나가
1535
+ * admin 을 강등하는 표면이 되면 그 강등은 마지막-admin 검사를 지나지
1536
+ * 않는다.
1537
+ *
1538
+ * 감사 `member.accept`.
1539
+ */
1540
+ post: operations["acceptInvite"];
1541
+ delete?: never;
1542
+ options?: never;
1543
+ head?: never;
1544
+ patch?: never;
1545
+ trace?: never;
1546
+ };
1416
1547
  "/v1/orgs/{orgSlug}/members/{userId}": {
1417
1548
  parameters: {
1418
1549
  query?: never;
@@ -1434,9 +1565,14 @@ export interface paths {
1434
1565
  put: operations["setMemberRole"];
1435
1566
  post?: never;
1436
1567
  /**
1437
- * 멤버를 뺀다 (`runlot org remove-member`)
1438
- * @description admin 이상. 마지막 admin 없다 (409 `last_admin`). 감사
1439
- * `member.remove`.
1568
+ * 멤버를 뺀다 · 스스로 나간다 (`runlot member rm`, `runlot member leave`)
1569
+ * @description **남을 빼는 것은 admin 이상, 자기를 빼는 것은 누구나** 수 있다
1570
+ * (`userId` 가 자기 자신이면 탈퇴다). admin 만 뺄 수 있게 두면 viewer 로
1571
+ * 초대된 사람이 조직을 나갈 길이 아예 없다.
1572
+ *
1573
+ * 마지막 admin 은 뺄 수도 나갈 수도 없다 (409 `last_admin`). 감사는
1574
+ * 제거가 `member.remove`, 탈퇴가 `member.leave` — 같은 문장이지만 다른
1575
+ * 사건이라 이름이 갈린다.
1440
1576
  */
1441
1577
  delete: operations["removeMember"];
1442
1578
  options?: never;
@@ -3026,6 +3162,53 @@ export interface components {
3026
3162
  /** Format: date-time */
3027
3163
  addedAt: string;
3028
3164
  };
3165
+ /**
3166
+ * @description `expired` 는 시각 비교의 결과이지 열이 아니다 — 초대는 만료되었다고
3167
+ * 표시되지 않고, 만료 시각이 지났을 뿐이다.
3168
+ * @enum {string}
3169
+ */
3170
+ InviteState: "pending" | "expired" | "accepted" | "revoked";
3171
+ /**
3172
+ * @description **토큰이 없다.** 평문 토큰은 메일에만 실리고, cp-public 은 그 열을 읽지도
3173
+ * 못한다 (마이그레이션 0043 의 열 단위 GRANT).
3174
+ */
3175
+ Invite: {
3176
+ /** Format: uuid */
3177
+ inviteId: string;
3178
+ /** Format: email */
3179
+ email: string;
3180
+ role: components["schemas"]["Role"];
3181
+ state: components["schemas"]["InviteState"];
3182
+ /** @description 부른 사람의 이름 또는 이메일. */
3183
+ invitedBy?: string;
3184
+ /** Format: date-time */
3185
+ createdAt: string;
3186
+ /** Format: date-time */
3187
+ expiresAt: string;
3188
+ /**
3189
+ * Format: date-time
3190
+ * @description 비어 있으면 아직 발송 큐에 있다 (발송은 cp-core 의 회차다).
3191
+ */
3192
+ sentAt?: string;
3193
+ /** @description 마지막 발송 실패 이유. 성공해도 남을 수 있다. */
3194
+ sendError?: string;
3195
+ };
3196
+ /** @description 토큰을 든 사람에게 보이는 초대 (인증 없이 답한다). */
3197
+ InviteView: {
3198
+ orgSlug: string;
3199
+ orgName: string;
3200
+ role: components["schemas"]["Role"];
3201
+ /**
3202
+ * Format: email
3203
+ * @description **초대를 보낸 주소**다. 수락하는 계정의 주소와 다를 수 있고, 화면은
3204
+ * 그 사실을 말해야 한다.
3205
+ */
3206
+ email: string;
3207
+ invitedBy?: string;
3208
+ state: components["schemas"]["InviteState"];
3209
+ /** Format: date-time */
3210
+ expiresAt: string;
3211
+ };
3029
3212
  /**
3030
3213
  * @description 워커의 `env.<NAME>` 이 된다. `DB`·`ASSETS`(바인딩 `env.db`·`env.assets` 의 대문자 짝) 와 `RUNLOT_` 접두는 예약이다
3031
3214
  * — 앞 둘은 이미 바인딩이고, 뒤는 노드가 프로세스 환경에 쓰는 이름
@@ -5954,6 +6137,169 @@ export interface operations {
5954
6137
  };
5955
6138
  };
5956
6139
  };
6140
+ listInvites: {
6141
+ parameters: {
6142
+ query?: never;
6143
+ header?: never;
6144
+ path: {
6145
+ orgSlug: components["parameters"]["OrgSlug"];
6146
+ };
6147
+ cookie?: never;
6148
+ };
6149
+ requestBody?: never;
6150
+ responses: {
6151
+ /** @description 초대 목록 */
6152
+ 200: {
6153
+ headers: {
6154
+ [name: string]: unknown;
6155
+ };
6156
+ content: {
6157
+ "application/json": {
6158
+ invites: components["schemas"]["Invite"][];
6159
+ };
6160
+ };
6161
+ };
6162
+ 403: components["responses"]["Forbidden"];
6163
+ 404: components["responses"]["NotFound"];
6164
+ };
6165
+ };
6166
+ createInvite: {
6167
+ parameters: {
6168
+ query?: never;
6169
+ header?: never;
6170
+ path: {
6171
+ orgSlug: components["parameters"]["OrgSlug"];
6172
+ };
6173
+ cookie?: never;
6174
+ };
6175
+ requestBody: {
6176
+ content: {
6177
+ "application/json": {
6178
+ /** Format: email */
6179
+ email: string;
6180
+ /** @description 기본값 `member`. */
6181
+ role?: components["schemas"]["Role"];
6182
+ };
6183
+ };
6184
+ };
6185
+ responses: {
6186
+ /** @description 기존 초대를 다시 보냈다 */
6187
+ 200: {
6188
+ headers: {
6189
+ [name: string]: unknown;
6190
+ };
6191
+ content: {
6192
+ "application/json": components["schemas"]["Invite"];
6193
+ };
6194
+ };
6195
+ /** @description 새 초대 */
6196
+ 201: {
6197
+ headers: {
6198
+ [name: string]: unknown;
6199
+ };
6200
+ content: {
6201
+ "application/json": components["schemas"]["Invite"];
6202
+ };
6203
+ };
6204
+ 400: components["responses"]["BadRequest"];
6205
+ 403: components["responses"]["Forbidden"];
6206
+ 404: components["responses"]["NotFound"];
6207
+ /** @description 이미 멤버다 (`already_member`) · 대기 초대가 너무 많다 (`too_many_invites`) */
6208
+ 409: {
6209
+ headers: {
6210
+ [name: string]: unknown;
6211
+ };
6212
+ content: {
6213
+ "application/json": components["schemas"]["Error"];
6214
+ };
6215
+ };
6216
+ };
6217
+ };
6218
+ revokeInvite: {
6219
+ parameters: {
6220
+ query?: never;
6221
+ header?: never;
6222
+ path: {
6223
+ orgSlug: components["parameters"]["OrgSlug"];
6224
+ inviteId: string;
6225
+ };
6226
+ cookie?: never;
6227
+ };
6228
+ requestBody?: never;
6229
+ responses: {
6230
+ /** @description 취소했다 */
6231
+ 204: {
6232
+ headers: {
6233
+ [name: string]: unknown;
6234
+ };
6235
+ content?: never;
6236
+ };
6237
+ 403: components["responses"]["Forbidden"];
6238
+ 404: components["responses"]["NotFound"];
6239
+ };
6240
+ };
6241
+ getInvite: {
6242
+ parameters: {
6243
+ query?: never;
6244
+ header?: never;
6245
+ path: {
6246
+ token: string;
6247
+ };
6248
+ cookie?: never;
6249
+ };
6250
+ requestBody?: never;
6251
+ responses: {
6252
+ /** @description 초대 */
6253
+ 200: {
6254
+ headers: {
6255
+ [name: string]: unknown;
6256
+ };
6257
+ content: {
6258
+ "application/json": components["schemas"]["InviteView"];
6259
+ };
6260
+ };
6261
+ 404: components["responses"]["NotFound"];
6262
+ };
6263
+ };
6264
+ acceptInvite: {
6265
+ parameters: {
6266
+ query?: never;
6267
+ header?: never;
6268
+ path: {
6269
+ token: string;
6270
+ };
6271
+ cookie?: never;
6272
+ };
6273
+ requestBody?: never;
6274
+ responses: {
6275
+ /** @description 수락했다 */
6276
+ 200: {
6277
+ headers: {
6278
+ [name: string]: unknown;
6279
+ };
6280
+ content: {
6281
+ "application/json": {
6282
+ orgSlug: string;
6283
+ orgName?: string;
6284
+ role: components["schemas"]["Role"];
6285
+ alreadyMember: boolean;
6286
+ invitedEmail?: string;
6287
+ };
6288
+ };
6289
+ };
6290
+ 401: components["responses"]["Unauthorized"];
6291
+ 404: components["responses"]["NotFound"];
6292
+ /** @description 이미 수락됨 (`invite_used`) · 취소됨 (`invite_revoked`) · 만료됨 (`invite_expired`) */
6293
+ 409: {
6294
+ headers: {
6295
+ [name: string]: unknown;
6296
+ };
6297
+ content: {
6298
+ "application/json": components["schemas"]["Error"];
6299
+ };
6300
+ };
6301
+ };
6302
+ };
5957
6303
  setMemberRole: {
5958
6304
  parameters: {
5959
6305
  query?: never;