rootr-cli 0.8.1 → 0.10.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/README.md +106 -1
- package/bin/rootr.js +532 -2
- package/lib/client.js +271 -7
- package/lib/mcp-tools/databases.js +421 -18
- package/lib/mcp-tools/documents.js +230 -3
- package/lib/mcp-tools/forms.js +87 -2
- package/lib/mcp-tools/misc.js +40 -0
- package/lib/mcp-tools/signed.js +467 -0
- package/lib/mcp-tools/workspaces.js +8 -1
- package/lib/mcp.js +13 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -348,6 +348,55 @@ export class RootrClient {
|
|
|
348
348
|
});
|
|
349
349
|
}
|
|
350
350
|
|
|
351
|
+
// ── DB-PERM-001 / DB-WATCH — 표에 거는 자물쇠와 알림 규칙 ──────────────
|
|
352
|
+
//
|
|
353
|
+
// 노션에 없는 것들이라, 에이전트가 표를 만들어 놓고도 "지원서는 서로 못 보게" 같은
|
|
354
|
+
// 요구를 못 들어주는 일이 있었다. 만들 수 있으면 잠글 수도 있어야 한다.
|
|
355
|
+
|
|
356
|
+
async getDatabasePermissions(databaseId) {
|
|
357
|
+
return this.request(`/databases/${encodeURIComponent(databaseId)}/permissions`);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
async setDatabasePermissions(databaseId, permissions) {
|
|
361
|
+
return this.request(`/databases/${encodeURIComponent(databaseId)}/permissions`, {
|
|
362
|
+
method: 'PATCH',
|
|
363
|
+
body: permissions,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async getDatabaseNotifications(databaseId) {
|
|
368
|
+
return this.request(`/databases/${encodeURIComponent(databaseId)}/notifications`);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async setDatabaseNotifications(databaseId, watchers) {
|
|
372
|
+
return this.request(`/databases/${encodeURIComponent(databaseId)}/notifications`, {
|
|
373
|
+
method: 'PUT',
|
|
374
|
+
body: { watchers },
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// ── PUBLIC-DOCS — 발행한 문서에 올라온 변경 제안 ────────────────────────
|
|
379
|
+
|
|
380
|
+
async listDocumentCommits(documentId, status = 'OPEN') {
|
|
381
|
+
return this.request(
|
|
382
|
+
`/documents/${encodeURIComponent(documentId)}/commits?status=${encodeURIComponent(status)}`,
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
async mergeDocumentCommit(documentId, commitId, force = false) {
|
|
387
|
+
return this.request(
|
|
388
|
+
`/documents/${encodeURIComponent(documentId)}/commits/${encodeURIComponent(commitId)}/merge`,
|
|
389
|
+
{ method: 'POST', body: force ? { force: true } : {} },
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
async rejectDocumentCommit(documentId, commitId, reason) {
|
|
394
|
+
return this.request(
|
|
395
|
+
`/documents/${encodeURIComponent(documentId)}/commits/${encodeURIComponent(commitId)}/reject`,
|
|
396
|
+
{ method: 'POST', body: reason ? { reason } : {} },
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
|
|
351
400
|
async getDocumentPublicStatus(documentId) {
|
|
352
401
|
return this.request(`/documents/${encodeURIComponent(documentId)}/public-status`);
|
|
353
402
|
}
|
|
@@ -595,13 +644,46 @@ export class RootrClient {
|
|
|
595
644
|
* building a table by adding columns one at a time made the order depend on
|
|
596
645
|
* the order of the calls, which is how migrated tables came out scrambled.
|
|
597
646
|
*/
|
|
598
|
-
async createDatabase(workspace, { name, parentId, path, createParents, autoRename, icon, properties, views, rows } = {}) {
|
|
647
|
+
async createDatabase(workspace, { name, parentId, path, createParents, autoRename, icon, properties, views, rows, rowPageType } = {}) {
|
|
599
648
|
return this.request(`/workspaces/${encodeURIComponent(workspace)}/databases`, {
|
|
600
649
|
method: 'POST',
|
|
601
|
-
body: { name, parentId, path, createParents, autoRename, icon, properties, views, rows },
|
|
650
|
+
body: { name, parentId, path, createParents, autoRename, icon, properties, views, rows, rowPageType },
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* SIGN-001 — 이 표의 행을 열었을 때 나오는 상세 문서를 서명문서로 바꾼다.
|
|
656
|
+
* 그 뒤로 만들어지는 행마다 서명문서(연구노트)가 붙는다.
|
|
657
|
+
*/
|
|
658
|
+
async setDatabaseRowPageType(databaseId, rowPageType) {
|
|
659
|
+
return this.request(`/databases/${encodeURIComponent(databaseId)}`, {
|
|
660
|
+
method: 'PATCH',
|
|
661
|
+
body: { rowPageType },
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* 뷰를 하나 **덧붙인다**(기존 뷰는 그대로). 표를 만들 때만 뷰를 정할 수 있어서,
|
|
667
|
+
* 노션에서 뷰가 넷인 표를 옮기면 하나밖에 못 두던 문제를 푼다.
|
|
668
|
+
*/
|
|
669
|
+
async createDatabaseView(
|
|
670
|
+
databaseId,
|
|
671
|
+
{ name, type, groupBy, dateProp, endDateProp, filters, sort, colWidths } = {},
|
|
672
|
+
) {
|
|
673
|
+
return this.request(`/databases/${encodeURIComponent(databaseId)}/views`, {
|
|
674
|
+
method: 'POST',
|
|
675
|
+
body: { name, type, groupBy, dateProp, endDateProp, filters, sort, colWidths },
|
|
602
676
|
});
|
|
603
677
|
}
|
|
604
678
|
|
|
679
|
+
/** 뷰 하나를 지운다. 마지막 하나는 못 지운다. 행 값은 안 지워진다. */
|
|
680
|
+
async deleteDatabaseView(databaseId, viewId) {
|
|
681
|
+
return this.request(
|
|
682
|
+
`/databases/${encodeURIComponent(databaseId)}/views/${encodeURIComponent(viewId)}`,
|
|
683
|
+
{ method: 'DELETE' },
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
|
|
605
687
|
/**
|
|
606
688
|
* 뷰 하나만 고친다(이름·감출 컬럼·그룹 기준). PATCH /databases/:id 는 views 배열을
|
|
607
689
|
* 통째로 갈아끼워서 빠뜨린 뷰가 사라지는데, 여기서는 서버가 그 뷰만 병합한다.
|
|
@@ -655,10 +737,11 @@ export class RootrClient {
|
|
|
655
737
|
// PATCH /databases/:id replaces the whole properties array, so rebuilding it
|
|
656
738
|
// client-side drops any column left out. These merge server-side instead.
|
|
657
739
|
|
|
658
|
-
async addColumn(databaseId, { name, type, options } = {}) {
|
|
740
|
+
async addColumn(databaseId, { name, type, options, relationDatabaseId, formula, rollup, prefix } = {}) {
|
|
659
741
|
return this.request(`/databases/${encodeURIComponent(databaseId)}/columns`, {
|
|
660
742
|
method: 'POST',
|
|
661
|
-
|
|
743
|
+
// 계산 칸(수식·롤업·번호)은 설정이 곧 그 칸이라 같이 보내야 한다.
|
|
744
|
+
body: { name, type, options, relationDatabaseId, formula, rollup, prefix },
|
|
662
745
|
});
|
|
663
746
|
}
|
|
664
747
|
|
|
@@ -1136,13 +1219,75 @@ export class RootrClient {
|
|
|
1136
1219
|
});
|
|
1137
1220
|
}
|
|
1138
1221
|
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1222
|
+
// ── 문서·폴더 접근 권한(공개범위) ────────────────────────────────
|
|
1223
|
+
// 화면의 "공개범위"·"권한" 창이 하는 일을 그대로 연다. 관리 권한이 있는
|
|
1224
|
+
// 사람만 부를 수 있고, 없으면 서버가 403으로 막는다.
|
|
1225
|
+
|
|
1226
|
+
/** 이 노드의 공개범위와 직접·상속 권한을 함께 읽는다. */
|
|
1227
|
+
async getNodeAccess(nodeId) {
|
|
1228
|
+
return this.request(`/nodes/${encodeURIComponent(nodeId)}/permissions`);
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
/** 공개범위를 정한다. null이면 상위 폴더/워크스페이스 기본값을 따른다. */
|
|
1232
|
+
async setNodeVisibility(nodeId, visibility) {
|
|
1233
|
+
return this.request(`/nodes/${encodeURIComponent(nodeId)}/visibility`, {
|
|
1234
|
+
method: 'POST',
|
|
1235
|
+
body: { visibility: visibility ?? null },
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
/** 특정 사람/그룹에게 이 노드의 권한을 준다(이미 있으면 등급만 바뀐다). */
|
|
1240
|
+
async grantNodePermission(nodeId, { subjectType = 'USER', subjectId, level }) {
|
|
1241
|
+
return this.request(`/nodes/${encodeURIComponent(nodeId)}/permissions`, {
|
|
1242
|
+
method: 'POST',
|
|
1243
|
+
body: { subjectType, subjectId, level },
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
/** 준 권한을 거둔다. */
|
|
1248
|
+
async revokeNodePermission(nodeId, { subjectType = 'USER', subjectId }) {
|
|
1249
|
+
return this.request(`/nodes/${encodeURIComponent(nodeId)}/permissions`, {
|
|
1250
|
+
method: 'DELETE',
|
|
1251
|
+
body: { subjectType, subjectId },
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
/**
|
|
1256
|
+
* Submitted responses, most recent first.
|
|
1257
|
+
* `latestPerRespondent`이면 같은 사람이 여러 번 낸 것 중 마지막 것만 남긴다
|
|
1258
|
+
* (누가 냈는지 모르는 익명 제출은 합치지 않는다 — 합칠 근거가 없다).
|
|
1259
|
+
*/
|
|
1260
|
+
async listFormResponses(formId, { latestPerRespondent } = {}) {
|
|
1261
|
+
const qs = latestPerRespondent ? '?latestPerRespondent=true' : '';
|
|
1262
|
+
const data = await this.request(`/forms/${encodeURIComponent(formId)}/responses${qs}`);
|
|
1142
1263
|
if (data && Array.isArray(data.responses)) return data.responses;
|
|
1143
1264
|
return [];
|
|
1144
1265
|
}
|
|
1145
1266
|
|
|
1267
|
+
/**
|
|
1268
|
+
* 응답 모을 곳을 새로 만들어 연결한다. 'database'면 폼 질문 그대로 칸이 생기고,
|
|
1269
|
+
* 'spreadsheet'면 응답이 깔리는 시트가 한 장 생긴다. 미리 만들어 둘 필요가 없다.
|
|
1270
|
+
*/
|
|
1271
|
+
async createFormCollector(formId, target) {
|
|
1272
|
+
return this.request(`/forms/${encodeURIComponent(formId)}/collect`, {
|
|
1273
|
+
method: 'POST',
|
|
1274
|
+
body: { target },
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
/** 이미 낸 응답의 내용을 고친다(폼의 검사 규칙을 그대로 다시 통과해야 한다). */
|
|
1279
|
+
async updateFormResponse(responseId, values) {
|
|
1280
|
+
return this.request(`/form-responses/${encodeURIComponent(responseId)}`, {
|
|
1281
|
+
method: 'PATCH',
|
|
1282
|
+
body: { values },
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
/** 응답 한 건을 지운다. */
|
|
1287
|
+
async deleteFormResponse(responseId) {
|
|
1288
|
+
return this.request(`/form-responses/${encodeURIComponent(responseId)}`, { method: 'DELETE' });
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1146
1291
|
async createFormResponse(formId, values) {
|
|
1147
1292
|
return this.request(`/forms/${encodeURIComponent(formId)}/responses`, {
|
|
1148
1293
|
method: 'POST',
|
|
@@ -1160,4 +1305,123 @@ export class RootrClient {
|
|
|
1160
1305
|
if (data && Array.isArray(data.links)) return data.links;
|
|
1161
1306
|
return [];
|
|
1162
1307
|
}
|
|
1308
|
+
|
|
1309
|
+
// ── SIGN-001: 서명문서 · 연구노트 ────────────────────────────────────
|
|
1310
|
+
//
|
|
1311
|
+
// 마크다운 문서인데 올리는 순간 본문이 얼고, 결재라인에 적힌 사람들이 차례로
|
|
1312
|
+
// 서명하면 서명마다 시점확인(TSA)과 해시 체이닝이 붙는다. 서명·올리기·반려는
|
|
1313
|
+
// 메일로 받은 6자리 확인 코드를 함께 보내야 한다 — 서버가 개인키를 보관하는
|
|
1314
|
+
// 구조에서 "본인이 눌렀다"의 유일한 증거라, 코드 없이 대신 눌러 줄 수는 없다.
|
|
1315
|
+
|
|
1316
|
+
/** 서명문서 만들기 — 본문·모드·결재라인까지 한 번에. */
|
|
1317
|
+
async createSignedDoc(workspaceId, body = {}) {
|
|
1318
|
+
return this.request(`/workspaces/${encodeURIComponent(workspaceId)}/signed-docs`, {
|
|
1319
|
+
method: 'POST',
|
|
1320
|
+
body,
|
|
1321
|
+
});
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
/** 지금 상태: 결재라인·단계별 진행·서명 목록·본문이 그대로인지. */
|
|
1325
|
+
async getSignedDoc(nodeId) {
|
|
1326
|
+
return this.request(`/signed-docs/${encodeURIComponent(nodeId)}`);
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
/** 결재라인 다시 짜기(올리기 전에만). steps 순서가 곧 서명 순서다. */
|
|
1330
|
+
async setApprovalLine(nodeId, steps) {
|
|
1331
|
+
return this.request(`/signed-docs/${encodeURIComponent(nodeId)}/approval-line`, {
|
|
1332
|
+
method: 'PUT',
|
|
1333
|
+
body: { steps },
|
|
1334
|
+
});
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
/** 연구노트 프리셋: 기재자(나) → 점검자 두 단계. */
|
|
1338
|
+
async applyResearchNotePreset(nodeId, { reviewerUserId, noteMeta } = {}) {
|
|
1339
|
+
return this.request(`/signed-docs/${encodeURIComponent(nodeId)}/research-note-preset`, {
|
|
1340
|
+
method: 'POST',
|
|
1341
|
+
body: { reviewerUserId, noteMeta },
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
/** 확인 코드를 본인 메일로 보낸다. 문서 하나용. */
|
|
1346
|
+
async requestSigningCode(nodeId) {
|
|
1347
|
+
return this.request(`/signed-docs/${encodeURIComponent(nodeId)}/request-code`, {
|
|
1348
|
+
method: 'POST',
|
|
1349
|
+
body: {},
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
/** 확인 코드 — 묶음 서명용(워크스페이스 단위로 한 번). */
|
|
1354
|
+
async requestBulkSigningCode(workspaceId) {
|
|
1355
|
+
return this.request(`/workspaces/${encodeURIComponent(workspaceId)}/signed-docs/request-code`, {
|
|
1356
|
+
method: 'POST',
|
|
1357
|
+
body: {},
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
/** 올리기 = 본문을 얼리고 기안자가 첫 도장을 찍는다. */
|
|
1362
|
+
async submitSignedDoc(nodeId, { code } = {}) {
|
|
1363
|
+
return this.request(`/signed-docs/${encodeURIComponent(nodeId)}/submit`, {
|
|
1364
|
+
method: 'POST',
|
|
1365
|
+
body: { code },
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
/** 내 차례에 서명한다. */
|
|
1370
|
+
async signSignedDoc(nodeId, { code } = {}) {
|
|
1371
|
+
return this.request(`/signed-docs/${encodeURIComponent(nodeId)}/sign`, {
|
|
1372
|
+
method: 'POST',
|
|
1373
|
+
body: { code },
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
/** 반려 — 사유는 필수다. 반려되면 다시 고칠 수 있게 풀린다. */
|
|
1378
|
+
async rejectSignedDoc(nodeId, { comment, code } = {}) {
|
|
1379
|
+
return this.request(`/signed-docs/${encodeURIComponent(nodeId)}/reject`, {
|
|
1380
|
+
method: 'POST',
|
|
1381
|
+
body: { comment, code },
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
/** 아직 아무도 서명하지 않은 초안을 다시 열어 고칠 수 있게 한다. */
|
|
1386
|
+
async reopenSignedDoc(nodeId) {
|
|
1387
|
+
return this.request(`/signed-docs/${encodeURIComponent(nodeId)}/reopen`, { method: 'POST' });
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
/** 개정본 만들기 — 서명된 문서는 고치는 게 아니라 새 판을 낸다. */
|
|
1391
|
+
async reviseSignedDoc(nodeId, note) {
|
|
1392
|
+
return this.request(`/signed-docs/${encodeURIComponent(nodeId)}/revise`, {
|
|
1393
|
+
method: 'POST',
|
|
1394
|
+
body: { note },
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
/** 위변조 검증. 로그인 없이도 열려 있다(본문은 안 내려온다). */
|
|
1399
|
+
async verifySignedDoc(nodeId) {
|
|
1400
|
+
return this.request(`/signed-docs/${encodeURIComponent(nodeId)}/verify`);
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
/** 워크스페이스 서명문서 목록. pendingMine이면 "내 차례인 것"만. */
|
|
1404
|
+
async listSignedDocs(workspaceId, { status, mode, pendingMine } = {}) {
|
|
1405
|
+
const qs = new URLSearchParams();
|
|
1406
|
+
if (status) qs.set('status', status);
|
|
1407
|
+
if (mode) qs.set('mode', mode);
|
|
1408
|
+
if (pendingMine) qs.set('pendingMine', 'true');
|
|
1409
|
+
const q = qs.toString();
|
|
1410
|
+
return this.request(
|
|
1411
|
+
`/workspaces/${encodeURIComponent(workspaceId)}/signed-docs${q ? `?${q}` : ''}`,
|
|
1412
|
+
);
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
/** 묶음 서명 — "이번 주 미점검 12건"을 한 번에. 코드 하나로 전부. */
|
|
1416
|
+
async bulkSignSignedDocs(workspaceId, { nodeIds, code } = {}) {
|
|
1417
|
+
return this.request(`/workspaces/${encodeURIComponent(workspaceId)}/signed-docs/bulk-sign`, {
|
|
1418
|
+
method: 'POST',
|
|
1419
|
+
body: { nodeIds, code },
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
/** 워크스페이스 원장(해시 체인) 전체 검증. */
|
|
1424
|
+
async verifyLedger(workspaceId) {
|
|
1425
|
+
return this.request(`/workspaces/${encodeURIComponent(workspaceId)}/ledger/verify`);
|
|
1426
|
+
}
|
|
1163
1427
|
}
|