@helpfeel/cosense-cli 1.13.0 → 1.14.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/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -42,6 +42,11 @@ import {
|
|
|
42
42
|
listPagesHelp,
|
|
43
43
|
listPagesSummary
|
|
44
44
|
} from './commands/listPages.ts';
|
|
45
|
+
import {
|
|
46
|
+
listPageSnapshots,
|
|
47
|
+
listPageSnapshotsHelp,
|
|
48
|
+
listPageSnapshotsSummary
|
|
49
|
+
} from './commands/listPageSnapshots.ts';
|
|
45
50
|
import {
|
|
46
51
|
listProjects,
|
|
47
52
|
listProjectsHelp,
|
|
@@ -68,6 +73,11 @@ import {
|
|
|
68
73
|
readPageHelp,
|
|
69
74
|
readPageSummary
|
|
70
75
|
} from './commands/readPage.ts';
|
|
76
|
+
import {
|
|
77
|
+
readPageSnapshot,
|
|
78
|
+
readPageSnapshotHelp,
|
|
79
|
+
readPageSnapshotSummary
|
|
80
|
+
} from './commands/readPageSnapshot.ts';
|
|
71
81
|
import {
|
|
72
82
|
readProjectMembers,
|
|
73
83
|
readProjectMembersHelp,
|
|
@@ -149,6 +159,11 @@ const commands: Record<string, CommandSpec> = {
|
|
|
149
159
|
help: browseRelatedPagesHelp
|
|
150
160
|
},
|
|
151
161
|
readPage: { handler: readPage, summary: readPageSummary, help: readPageHelp },
|
|
162
|
+
readPageSnapshot: {
|
|
163
|
+
handler: readPageSnapshot,
|
|
164
|
+
summary: readPageSnapshotSummary,
|
|
165
|
+
help: readPageSnapshotHelp
|
|
166
|
+
},
|
|
152
167
|
readFileInfo: {
|
|
153
168
|
handler: readFileInfo,
|
|
154
169
|
summary: readFileInfoSummary,
|
|
@@ -179,6 +194,11 @@ const commands: Record<string, CommandSpec> = {
|
|
|
179
194
|
summary: listPagesSummary,
|
|
180
195
|
help: listPagesHelp
|
|
181
196
|
},
|
|
197
|
+
listPageSnapshots: {
|
|
198
|
+
handler: listPageSnapshots,
|
|
199
|
+
summary: listPageSnapshotsSummary,
|
|
200
|
+
help: listPageSnapshotsHelp
|
|
201
|
+
},
|
|
182
202
|
list1hopLinks: {
|
|
183
203
|
handler: list1hopLinks,
|
|
184
204
|
summary: list1hopLinksSummary,
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { enrichTimestampsOf } from '../lib/enrichTimestamps.ts';
|
|
2
|
+
import { parseProjectUrlStrict } from '../lib/parseUrl.ts';
|
|
3
|
+
import { requestJson } from '../lib/request.ts';
|
|
4
|
+
import { resolveCredential } from '../lib/settings.ts';
|
|
5
|
+
|
|
6
|
+
export const listPageSnapshotsSummary =
|
|
7
|
+
'ページのsnapshot一覧(Page History)をpageId起点で取得する';
|
|
8
|
+
|
|
9
|
+
export const listPageSnapshotsHelp = `listPageSnapshots - ページのsnapshot一覧(Page History)をpageId起点で取得する
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
cosense listPageSnapshots <projectUrl> <pageId>
|
|
13
|
+
|
|
14
|
+
引数:
|
|
15
|
+
<projectUrl> プロジェクトのURL (例: https://scrapbox.io/shokai)。 末尾に余分なpathがあるとerror
|
|
16
|
+
<pageId> ページの不変ID。browsePage / readPage の出力に含まれる
|
|
17
|
+
|
|
18
|
+
戻り値(top-levelのkey):
|
|
19
|
+
pageId string 対象ページのID
|
|
20
|
+
timestamps Array<{ id, created }> snapshotの一覧。新しい順、最新100件まで
|
|
21
|
+
id string snapshotのID。readPageSnapshot に渡す
|
|
22
|
+
created string snapshot時点のページ最終更新時刻
|
|
23
|
+
|
|
24
|
+
戻り値のJSON抜粋例:
|
|
25
|
+
{
|
|
26
|
+
"pageId": "5803c5397ad353b0aee24341",
|
|
27
|
+
"timestamps": [
|
|
28
|
+
{
|
|
29
|
+
"id": "5cef4052dd15ed00447c1405",
|
|
30
|
+
"created": "2019-05-30T11:29+09:00 (7 years ago)"
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
HTTPエラー:
|
|
36
|
+
HTTP 401 認証なし
|
|
37
|
+
HTTP 403 権限不足(projectのmemberでない 等)
|
|
38
|
+
HTTP 404 pageId に対応するpageが存在しない / pageId が不正な形式
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
interface ListPageSnapshotsData {
|
|
42
|
+
timestamps?: { id?: string; created?: number | string }[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const listPageSnapshots = async (args: string[]): Promise<void> => {
|
|
46
|
+
if (args.length !== 2) {
|
|
47
|
+
throw new Error('Usage: cosense listPageSnapshots <projectUrl> <pageId>');
|
|
48
|
+
}
|
|
49
|
+
const [projectUrl, pageId] = args as [string, string];
|
|
50
|
+
|
|
51
|
+
const { origin, projectName } = parseProjectUrlStrict(projectUrl);
|
|
52
|
+
const credential = resolveCredential(origin, projectName);
|
|
53
|
+
|
|
54
|
+
// APIはx-following-idレスポンスヘッダで100件ずつページングするが、最新100件のみ返し追跡しない
|
|
55
|
+
const apiUrl = `${origin}/api/page-snapshots/${projectName}/${pageId}`;
|
|
56
|
+
const data = (await requestJson(apiUrl, {
|
|
57
|
+
credential
|
|
58
|
+
})) as ListPageSnapshotsData;
|
|
59
|
+
|
|
60
|
+
for (const timestamp of data.timestamps ?? []) {
|
|
61
|
+
enrichTimestampsOf(timestamp as Record<string, unknown>, ['created']);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
|
|
65
|
+
};
|
|
@@ -15,7 +15,9 @@ Usage:
|
|
|
15
15
|
<pageId> 削除対象ページのID。 readPage 出力の top-level "id" field から取得する
|
|
16
16
|
|
|
17
17
|
戻り値(plain text):
|
|
18
|
-
previewId / expireAt / status (delete) / project / title のヘッダー +
|
|
18
|
+
previewId / expireAt / status (delete) / project / title / commitId のヘッダー +
|
|
19
|
+
削除されるpage全体。
|
|
20
|
+
commitId は preview を作った時点の削除対象ページの最新コミット。
|
|
19
21
|
preview は dry-run なのでこの段階では削除されない。
|
|
20
22
|
previewId を submitEdit に渡すと削除が確定する。5分で expire する。
|
|
21
23
|
|
|
@@ -27,6 +29,7 @@ HTTPエラー:
|
|
|
27
29
|
|
|
28
30
|
interface PagePreview {
|
|
29
31
|
title?: string;
|
|
32
|
+
commitId?: string;
|
|
30
33
|
lines?: { id: string; text: string }[];
|
|
31
34
|
}
|
|
32
35
|
|
|
@@ -67,6 +70,7 @@ export const previewDelete = async (args: string[]): Promise<void> => {
|
|
|
67
70
|
lines.push('status: delete');
|
|
68
71
|
lines.push(`project: ${projectName}`);
|
|
69
72
|
lines.push(`title: ${response.pagePreview?.title ?? ''}`);
|
|
73
|
+
lines.push(`commitId: ${response.pagePreview?.commitId ?? ''}`);
|
|
70
74
|
lines.push('');
|
|
71
75
|
lines.push('page (will be deleted):');
|
|
72
76
|
for (const line of response.pagePreview?.lines ?? []) {
|
|
@@ -47,8 +47,10 @@ stdinから受け取る入力形式(既存ページ編集モード, JSON):
|
|
|
47
47
|
する必要がある(消えた lineId を anchor に指定すると 422)。
|
|
48
48
|
|
|
49
49
|
戻り値(plain text):
|
|
50
|
-
previewId / expireAt / status (create or update) / project / title のヘッダー +
|
|
50
|
+
previewId / expireAt / status (create or update) / project / title / commitId のヘッダー +
|
|
51
51
|
ops summary + 適用後 page 全体。
|
|
52
|
+
commitId は preview を作った時点の編集対象ページの最新コミット。 新規作成 (status: create)
|
|
53
|
+
では出力されない。
|
|
52
54
|
変更行の頭には > (新規) または * (更新) のマーカーと末尾 # <lineId> が付く。
|
|
53
55
|
既存行(変更なし)はマーカーなし。
|
|
54
56
|
preview は dry-run なのでこの段階ではpage URLは確定しない。 確定URLは submitEdit の出力で確認する。
|
|
@@ -98,6 +100,7 @@ type RawChange = RawInsertChange | RawUpdateChange | RawDeleteChange;
|
|
|
98
100
|
interface PagePreview {
|
|
99
101
|
title?: string;
|
|
100
102
|
persistent?: boolean;
|
|
103
|
+
commitId?: string;
|
|
101
104
|
lines?: { id: string; text: string }[];
|
|
102
105
|
}
|
|
103
106
|
|
|
@@ -212,6 +215,10 @@ const formatPreview = (
|
|
|
212
215
|
lines.push(`status: ${status}`);
|
|
213
216
|
lines.push(`project: ${projectName}`);
|
|
214
217
|
lines.push(`title: ${title}`);
|
|
218
|
+
// 新規作成 (persistent: false) のページにはまだcommitが無く、サーバーもcommitIdを返さない
|
|
219
|
+
if (typeof pagePreview?.commitId === 'string') {
|
|
220
|
+
lines.push(`commitId: ${pagePreview.commitId}`);
|
|
221
|
+
}
|
|
215
222
|
lines.push('');
|
|
216
223
|
lines.push('ops:');
|
|
217
224
|
for (const op of ops) {
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { enrichTimestampsOf } from '../lib/enrichTimestamps.ts';
|
|
2
|
+
import { parseProjectUrlStrict } from '../lib/parseUrl.ts';
|
|
3
|
+
import { requestJson } from '../lib/request.ts';
|
|
4
|
+
import { enrichUser, fetchUserMap } from '../lib/resolveUsers.ts';
|
|
5
|
+
import { resolveCredential } from '../lib/settings.ts';
|
|
6
|
+
|
|
7
|
+
export const readPageSnapshotSummary =
|
|
8
|
+
'snapshotのIDを指定してPage History上の過去の本文を読む';
|
|
9
|
+
|
|
10
|
+
export const readPageSnapshotHelp = `readPageSnapshot - snapshotのIDを指定してPage History上の過去の本文を読む
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
cosense readPageSnapshot <projectUrl> <pageId> <snapshotId>
|
|
14
|
+
|
|
15
|
+
引数:
|
|
16
|
+
<projectUrl> プロジェクトのURL (例: https://scrapbox.io/shokai)。 末尾に余分なpathがあるとerror
|
|
17
|
+
<pageId> ページの不変ID。browsePage / readPage の出力に含まれる
|
|
18
|
+
<snapshotId> snapshotのID。listPageSnapshots の timestamps[].id から取得する
|
|
19
|
+
|
|
20
|
+
戻り値(top-levelのkey):
|
|
21
|
+
page object 現在のページのメタデータ。field構成は readPage のtop-levelから本文・リンク系
|
|
22
|
+
(lines / links / icons 等) を除いたもの。現在の本文は含まれない
|
|
23
|
+
snapshot object snapshot時点のページ
|
|
24
|
+
title string snapshot時点のページタイトル
|
|
25
|
+
created string snapshot時点のページ最終更新時刻
|
|
26
|
+
lines Array<Line> snapshot時点の本文。Line = { id, text, user, created, updated }
|
|
27
|
+
|
|
28
|
+
戻り値のJSON抜粋例:
|
|
29
|
+
{
|
|
30
|
+
"page": {
|
|
31
|
+
"id": "5803c5397ad353b0aee24341",
|
|
32
|
+
"title": "page1",
|
|
33
|
+
"commitId": "5cef401cfca37d0018dd9ead"
|
|
34
|
+
},
|
|
35
|
+
"snapshot": {
|
|
36
|
+
"title": "page1",
|
|
37
|
+
"created": "2019-05-30T11:29+09:00 (7 years ago)",
|
|
38
|
+
"lines": [
|
|
39
|
+
{
|
|
40
|
+
"id": "5803c5397ad353b0aee24341",
|
|
41
|
+
"text": "page1",
|
|
42
|
+
"user": { "id": "5803c4bd7ad353b0aee24328", "name": "shokai" },
|
|
43
|
+
"created": "2019-05-30T11:29+09:00 (7 years ago)",
|
|
44
|
+
"updated": "2019-05-30T11:29+09:00 (7 years ago)"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
絞り込み例(jqで欲しい部分だけ抜き出す):
|
|
51
|
+
snapshot時点の各行のテキストだけ:
|
|
52
|
+
cosense readPageSnapshot <projectUrl> <pageId> <snapshotId> | jq -r '.snapshot.lines[].text'
|
|
53
|
+
|
|
54
|
+
HTTPエラー:
|
|
55
|
+
HTTP 401 認証なし
|
|
56
|
+
HTTP 403 権限不足(projectのmemberでない 等)
|
|
57
|
+
HTTP 404 pageId に対応するpageが存在しない / snapshotId がこのページのsnapshotでない
|
|
58
|
+
HTTP 422 snapshotId が不正な形式
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
interface UserRef {
|
|
62
|
+
id: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface SnapshotLine {
|
|
66
|
+
id?: string;
|
|
67
|
+
text?: string;
|
|
68
|
+
userId?: string;
|
|
69
|
+
user?: UserRef;
|
|
70
|
+
created?: number | string;
|
|
71
|
+
updated?: number | string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface PageMetadata {
|
|
75
|
+
user?: UserRef | null;
|
|
76
|
+
lastUpdateUser?: UserRef | null;
|
|
77
|
+
users?: UserRef[];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface ReadPageSnapshotData {
|
|
81
|
+
page?: PageMetadata;
|
|
82
|
+
snapshot?: {
|
|
83
|
+
created?: number | string;
|
|
84
|
+
lines?: SnapshotLine[];
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const readPageSnapshot = async (args: string[]): Promise<void> => {
|
|
89
|
+
if (args.length !== 3) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
'Usage: cosense readPageSnapshot <projectUrl> <pageId> <snapshotId>'
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
const [projectUrl, pageId, snapshotId] = args as [string, string, string];
|
|
95
|
+
|
|
96
|
+
const { origin, projectName } = parseProjectUrlStrict(projectUrl);
|
|
97
|
+
const credential = resolveCredential(origin, projectName);
|
|
98
|
+
|
|
99
|
+
const apiUrl = `${origin}/api/page-snapshots/${projectName}/${pageId}/${snapshotId}`;
|
|
100
|
+
const data = (await requestJson(apiUrl, {
|
|
101
|
+
credential
|
|
102
|
+
})) as ReadPageSnapshotData;
|
|
103
|
+
|
|
104
|
+
const userMap = await fetchUserMap(origin, projectName);
|
|
105
|
+
const page = data.page;
|
|
106
|
+
if (page) {
|
|
107
|
+
enrichUser(page.user, userMap);
|
|
108
|
+
enrichUser(page.lastUpdateUser, userMap);
|
|
109
|
+
for (const editor of page.users ?? []) {
|
|
110
|
+
enrichUser(editor, userMap);
|
|
111
|
+
}
|
|
112
|
+
enrichTimestampsOf(page as Record<string, unknown>, [
|
|
113
|
+
'created',
|
|
114
|
+
'updated',
|
|
115
|
+
'accessed',
|
|
116
|
+
'snapshotCreated',
|
|
117
|
+
'lastAccessed'
|
|
118
|
+
]);
|
|
119
|
+
}
|
|
120
|
+
const snapshot = data.snapshot;
|
|
121
|
+
if (snapshot) {
|
|
122
|
+
for (const line of snapshot.lines ?? []) {
|
|
123
|
+
const userId = line.userId;
|
|
124
|
+
if (typeof userId === 'string' && userId !== '') {
|
|
125
|
+
line.user = { id: userId };
|
|
126
|
+
delete line.userId;
|
|
127
|
+
enrichUser(line.user, userMap);
|
|
128
|
+
}
|
|
129
|
+
enrichTimestampsOf(line as Record<string, unknown>, [
|
|
130
|
+
'created',
|
|
131
|
+
'updated'
|
|
132
|
+
]);
|
|
133
|
+
}
|
|
134
|
+
enrichTimestampsOf(snapshot as Record<string, unknown>, ['created']);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
|
|
138
|
+
};
|