@eightstate/escli 0.5.0 → 0.7.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/commands/notion/block/trash.js +71 -0
- package/dist/commands/notion/comments/add.js +48 -0
- package/dist/commands/notion/comments/get.js +42 -0
- package/dist/commands/notion/comments/list.js +55 -0
- package/dist/commands/notion/comments/reply.js +45 -0
- package/dist/commands/notion/comments/thread.js +87 -0
- package/dist/commands/notion/db/create.js +555 -0
- package/dist/commands/notion/db/query.js +451 -0
- package/dist/commands/notion/db/row/create.js +74 -0
- package/dist/commands/notion/db/row/update.js +165 -0
- package/dist/commands/notion/ds/create.js +73 -0
- package/dist/commands/notion/enroll.js +302 -0
- package/dist/commands/notion/index.js +24 -0
- package/dist/commands/notion/page/edit.js +73 -0
- package/dist/commands/notion/page/move.js +59 -0
- package/dist/commands/notion/page/read.js +60 -0
- package/dist/commands/notion/page/replace-content.js +80 -0
- package/dist/commands/notion/page/replace-text.js +80 -0
- package/dist/commands/notion/page/replace.js +63 -0
- package/dist/commands/notion/page/trash.js +79 -0
- package/dist/commands/notion/search.js +207 -0
- package/dist/commands/notion/upload/attach.js +105 -0
- package/dist/commands/notion/upload/index.js +129 -0
- package/dist/commands/notion/upload/list.js +78 -0
- package/dist/commands/notion/upload/status.js +76 -0
- package/dist/commands/notion/view/create.js +78 -0
- package/dist/commands/notion/whoami.js +96 -0
- package/dist/commands/research.js +11 -0
- package/dist/entry.js +12 -6
- package/dist/io/render-kv.js +12 -0
- package/dist/io/render-labeled.js +16 -0
- package/dist/io/render-table.js +19 -0
- package/dist/io/render-trailer.js +7 -0
- package/dist/lib/manifest.js +1 -1
- package/dist/lib/notion/comments/shared.js +366 -0
- package/dist/lib/notion/db-row/common.js +367 -0
- package/dist/lib/notion/manifest-pass.js +4 -0
- package/dist/lib/notion/page/content-common.js +473 -0
- package/dist/lib/notion/trash-move/support.js +300 -0
- package/dist/lib/notion/upload/shared.js +372 -0
- package/dist/lib/registry.js +118 -25
- package/dist/services/notion.js +274 -0
- package/dist/services/research.js +31 -10
- package/oclif.manifest.json +4084 -1
- package/package.json +22 -17
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import { notionBlocksDelete, notionBlocksUpdate } from '../../../services/notion.js';
|
|
3
|
+
import { blockRef, botName, formatParentCompact, notionErrors, NotionWriteCommand, parentRef, parseNotionId, recordValue, renderReceipt, requireYes, } from '../../../lib/notion/trash-move/support.js';
|
|
4
|
+
export default class NotionBlockTrash extends NotionWriteCommand {
|
|
5
|
+
static errors = notionErrors();
|
|
6
|
+
static summary = 'Move a Notion block to Trash, or restore it with --restore';
|
|
7
|
+
static description = 'Soft-trash or restore a Notion block through the enrolled Notion bot. Block trash uses Notion DELETE, which is a soft delete.';
|
|
8
|
+
static examples = [
|
|
9
|
+
'<%= config.bin %> notion block trash 2ab6c741-4f85-4b17-9f88-e938fb5d0b66 --yes',
|
|
10
|
+
'<%= config.bin %> notion block trash 2ab6c741-4f85-4b17-9f88-e938fb5d0b66 --restore --yes',
|
|
11
|
+
];
|
|
12
|
+
static aliases = [];
|
|
13
|
+
static flags = {
|
|
14
|
+
restore: Flags.boolean({ description: 'Restore the block instead of trashing it.', default: false }),
|
|
15
|
+
yes: Flags.boolean({ description: 'Confirm the destructive write without prompting.', default: false }),
|
|
16
|
+
raw: Flags.boolean({ description: 'Emit the verbatim Notion response body.', default: false }),
|
|
17
|
+
};
|
|
18
|
+
static args = {
|
|
19
|
+
block: Args.string({ description: 'Block ID.', required: true }),
|
|
20
|
+
};
|
|
21
|
+
static enableJsonFlag = true;
|
|
22
|
+
static strict = true;
|
|
23
|
+
async execute() {
|
|
24
|
+
const { args, flags } = await this.parse(NotionBlockTrash);
|
|
25
|
+
this.outputFlags = flags;
|
|
26
|
+
requireYes(flags.yes);
|
|
27
|
+
const blockId = parseNotionId(args.block);
|
|
28
|
+
const result = flags.restore ? await notionBlocksUpdate(blockId, { in_trash: false }) : await notionBlocksDelete(blockId);
|
|
29
|
+
const raw = this.rememberResult(result);
|
|
30
|
+
const block = blockRef(raw, blockId);
|
|
31
|
+
const parent = parentRef(recordValue(raw)?.parent);
|
|
32
|
+
const bot = botName(raw);
|
|
33
|
+
if (flags.restore) {
|
|
34
|
+
return {
|
|
35
|
+
action: 'block.restore',
|
|
36
|
+
block,
|
|
37
|
+
state: { before: 'in_trash', after: 'active' },
|
|
38
|
+
restored_under: parent,
|
|
39
|
+
...(bot ? { bot } : {}),
|
|
40
|
+
notion_version: '2026-03-11',
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
action: 'block.trash',
|
|
45
|
+
block,
|
|
46
|
+
state: { before: 'active', after: 'in_trash' },
|
|
47
|
+
destructive: true,
|
|
48
|
+
reversible: true,
|
|
49
|
+
undo: `escli notion block trash ${block.id} --restore --yes`,
|
|
50
|
+
...(bot ? { bot } : {}),
|
|
51
|
+
notion_version: '2026-03-11',
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
render(data) {
|
|
55
|
+
const baseRows = [
|
|
56
|
+
['action', data.action],
|
|
57
|
+
['block', `${data.block.type} ${data.block.id}`],
|
|
58
|
+
['preview', data.block.preview],
|
|
59
|
+
['state', `${data.state.before} → ${data.state.after}`],
|
|
60
|
+
];
|
|
61
|
+
if (data.action === 'block.restore') {
|
|
62
|
+
baseRows.push(['restored_under', formatParentCompact(data.restored_under)]);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
baseRows.push(['destructive', true], ['reversible', true], ['undo', data.undo ?? '']);
|
|
66
|
+
}
|
|
67
|
+
baseRows.push(['bot', data.bot ?? ''], ['notion_version', data.notion_version]);
|
|
68
|
+
return renderReceipt(baseRows);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=trash.js.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import { renderKv } from '../../../io/render-kv.js';
|
|
3
|
+
import { notionCommentsCreate } from '../../../services/notion.js';
|
|
4
|
+
import { createBodyFromMarkdown, formatAuthor, formatCreated, joinMarkdown, kvEntries, notionCommentErrors, NotionCommentsCommand, parseTarget, shapeReceipt, } from '../../../lib/notion/comments/shared.js';
|
|
5
|
+
export default class NotionCommentsAdd extends NotionCommentsCommand {
|
|
6
|
+
static errors = notionCommentErrors;
|
|
7
|
+
static summary = 'Add a Notion comment to a page or block';
|
|
8
|
+
static description = 'Create a new page-level or block-level Notion comment as the bot.';
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> notion comments add 362e1f8b-0d45-4d7b-bd85-cfb7c26f00dc "Checked: the pricing table now matches the latest deck."',
|
|
11
|
+
'<%= config.bin %> notion comments add 8c3f2d49-3a4b-4f0f-8d2e-208a7623b44a "This sentence is stale; source deck says $249." --raw',
|
|
12
|
+
];
|
|
13
|
+
static aliases = [];
|
|
14
|
+
static flags = {
|
|
15
|
+
raw: Flags.boolean({ description: 'Emit the verbatim Notion response body.', default: false }),
|
|
16
|
+
};
|
|
17
|
+
static args = {
|
|
18
|
+
target: Args.string({ description: 'Page or block ID/URL.', required: true }),
|
|
19
|
+
markdown: Args.string({ description: 'Inline comment markdown.', required: true, multiple: true }),
|
|
20
|
+
};
|
|
21
|
+
static enableJsonFlag = true;
|
|
22
|
+
static strict = true;
|
|
23
|
+
async execute() {
|
|
24
|
+
const { args, flags } = await this.parse(NotionCommentsAdd);
|
|
25
|
+
const target = parseTarget(args.target);
|
|
26
|
+
const markdown = joinMarkdown(args.markdown);
|
|
27
|
+
const result = await notionCommentsCreate({ body: createBodyFromMarkdown(target, markdown) });
|
|
28
|
+
this.setNext(null);
|
|
29
|
+
if (flags.raw)
|
|
30
|
+
return this.setRaw(result.raw);
|
|
31
|
+
const action = target.kind === 'block' ? 'created_block_comment' : 'created_page_comment';
|
|
32
|
+
return shapeReceipt(result, { action, where: `${target.kind} ${target.id}`, body: markdown });
|
|
33
|
+
}
|
|
34
|
+
render(data) {
|
|
35
|
+
const receipt = data;
|
|
36
|
+
return renderKv(kvEntries({
|
|
37
|
+
created: receipt.created,
|
|
38
|
+
discussion_id: receipt.discussion_id,
|
|
39
|
+
action: receipt.action,
|
|
40
|
+
where: receipt.where,
|
|
41
|
+
author: formatAuthor({ author: receipt.author, author_kind: receipt.author_kind }),
|
|
42
|
+
created_time: formatCreated(receipt.created_time),
|
|
43
|
+
warning: receipt.warning,
|
|
44
|
+
body: receipt.body,
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=add.js.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import { renderKv } from '../../../io/render-kv.js';
|
|
3
|
+
import { proxy } from '../../../services/notion.js';
|
|
4
|
+
import { formatAuthor, formatCreated, kvEntries, notionCommentErrors, NotionCommentsCommand, shapeGet } from '../../../lib/notion/comments/shared.js';
|
|
5
|
+
export default class NotionCommentsGet extends NotionCommentsCommand {
|
|
6
|
+
static errors = notionCommentErrors;
|
|
7
|
+
static summary = 'Get one Notion comment by ID';
|
|
8
|
+
static description = 'UNVERIFIED — GET /v1/comments/{id}; live-probe validates whether the endpoint exists before relying on this command.';
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> notion comments get cmt_7f2d',
|
|
11
|
+
'<%= config.bin %> notion comments get cmt_7f2d --raw',
|
|
12
|
+
];
|
|
13
|
+
static aliases = [];
|
|
14
|
+
static flags = {
|
|
15
|
+
raw: Flags.boolean({ description: 'Emit the verbatim Notion response body.', default: false }),
|
|
16
|
+
};
|
|
17
|
+
static args = {
|
|
18
|
+
commentId: Args.string({ description: 'Comment ID.', required: true }),
|
|
19
|
+
};
|
|
20
|
+
static enableJsonFlag = true;
|
|
21
|
+
static strict = true;
|
|
22
|
+
async execute() {
|
|
23
|
+
const { args, flags } = await this.parse(NotionCommentsGet);
|
|
24
|
+
const result = await proxy('GET', `/v1/comments/${encodeURIComponent(args.commentId)}`);
|
|
25
|
+
this.setNext(null);
|
|
26
|
+
if (flags.raw)
|
|
27
|
+
return this.setRaw(result.raw);
|
|
28
|
+
return shapeGet(result);
|
|
29
|
+
}
|
|
30
|
+
render(data) {
|
|
31
|
+
const comment = data;
|
|
32
|
+
return renderKv(kvEntries({
|
|
33
|
+
id: comment.id,
|
|
34
|
+
discussion_id: comment.discussion_id,
|
|
35
|
+
where: comment.where,
|
|
36
|
+
author: formatAuthor(comment),
|
|
37
|
+
created: formatCreated(comment.created),
|
|
38
|
+
body: comment.body ?? comment.snippet,
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=get.js.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import { renderKv } from '../../../io/render-kv.js';
|
|
3
|
+
import { renderTable } from '../../../io/render-table.js';
|
|
4
|
+
import { notionCommentsList } from '../../../services/notion.js';
|
|
5
|
+
import { formatAuthor, formatCreated, formatTarget, notionCommentErrors, NotionCommentsCommand, parseTarget, renderContinuation, shapeList, } from '../../../lib/notion/comments/shared.js';
|
|
6
|
+
export default class NotionCommentsList extends NotionCommentsCommand {
|
|
7
|
+
static errors = notionCommentErrors;
|
|
8
|
+
static summary = 'List Notion comments for a page or block';
|
|
9
|
+
static description = 'List open Notion comments attached to a page or block. Paginated; repeat with --next to continue.';
|
|
10
|
+
static examples = [
|
|
11
|
+
'<%= config.bin %> notion comments list 362e1f8b-0d45-4d7b-bd85-cfb7c26f00dc',
|
|
12
|
+
'<%= config.bin %> notion comments list https://notion.so/acme/Launch-Plan-362e?pvs=4 --next eyJ2Ijox',
|
|
13
|
+
'<%= config.bin %> notion comments list 362e1f8b-0d45-4d7b-bd85-cfb7c26f00dc --raw',
|
|
14
|
+
];
|
|
15
|
+
static aliases = [];
|
|
16
|
+
static flags = {
|
|
17
|
+
next: Flags.string({ description: 'Notion pagination token from the previous response.' }),
|
|
18
|
+
raw: Flags.boolean({ description: 'Emit the verbatim Notion response body.', default: false }),
|
|
19
|
+
};
|
|
20
|
+
static args = {
|
|
21
|
+
target: Args.string({ description: 'Page or block ID/URL.', required: true }),
|
|
22
|
+
};
|
|
23
|
+
static enableJsonFlag = true;
|
|
24
|
+
static strict = true;
|
|
25
|
+
async execute() {
|
|
26
|
+
const { args, flags } = await this.parse(NotionCommentsList);
|
|
27
|
+
const target = parseTarget(args.target);
|
|
28
|
+
const result = await notionCommentsList(target.id, flags.next);
|
|
29
|
+
this.setNext(result.meta.next);
|
|
30
|
+
if (flags.raw)
|
|
31
|
+
return this.setRaw(result.raw);
|
|
32
|
+
return shapeList(result, target);
|
|
33
|
+
}
|
|
34
|
+
render(data) {
|
|
35
|
+
const shaped = data;
|
|
36
|
+
const header = renderKv({
|
|
37
|
+
target: formatTarget(shaped.target),
|
|
38
|
+
shown: `${shaped.summary.shown} ${shaped.summary.shown === 1 ? 'comment' : 'comments'} · threads: ${shaped.summary.threads} · human: ${shaped.summary.human_comments} · bot: ${shaped.summary.bot_comments}`,
|
|
39
|
+
});
|
|
40
|
+
const table = renderTable({
|
|
41
|
+
header: ['id', 'discussion', 'where', 'author', 'created', 'snippet'],
|
|
42
|
+
rows: shaped.comments.map((comment) => [
|
|
43
|
+
comment.id,
|
|
44
|
+
comment.discussion_id,
|
|
45
|
+
comment.where,
|
|
46
|
+
formatAuthor(comment),
|
|
47
|
+
formatCreated(comment.created, true),
|
|
48
|
+
comment.snippet,
|
|
49
|
+
]),
|
|
50
|
+
});
|
|
51
|
+
const trailer = renderContinuation(shaped.next, shaped.remaining);
|
|
52
|
+
return [header, table, trailer].filter(Boolean).join('\n\n');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import { renderKv } from '../../../io/render-kv.js';
|
|
3
|
+
import { notionCommentsCreate } from '../../../services/notion.js';
|
|
4
|
+
import { formatAuthor, formatCreated, joinMarkdown, kvEntries, notionCommentErrors, NotionCommentsCommand, replyBodyFromMarkdown, shapeReceipt, } from '../../../lib/notion/comments/shared.js';
|
|
5
|
+
export default class NotionCommentsReply extends NotionCommentsCommand {
|
|
6
|
+
static errors = notionCommentErrors;
|
|
7
|
+
static summary = 'Reply to a Notion discussion thread';
|
|
8
|
+
static description = 'Append a bot reply to an existing Notion discussion_id.';
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> notion comments reply disc_6a91 "Confirmed — I updated the pricing table to match the deck."',
|
|
11
|
+
'<%= config.bin %> notion comments reply disc_6a91 "Confirmed — I updated the pricing table to match the deck." --raw',
|
|
12
|
+
];
|
|
13
|
+
static aliases = [];
|
|
14
|
+
static flags = {
|
|
15
|
+
raw: Flags.boolean({ description: 'Emit the verbatim Notion response body.', default: false }),
|
|
16
|
+
};
|
|
17
|
+
static args = {
|
|
18
|
+
discussionId: Args.string({ description: 'Discussion ID.', required: true }),
|
|
19
|
+
markdown: Args.string({ description: 'Inline comment markdown.', required: true, multiple: true }),
|
|
20
|
+
};
|
|
21
|
+
static enableJsonFlag = true;
|
|
22
|
+
static strict = true;
|
|
23
|
+
async execute() {
|
|
24
|
+
const { args, flags } = await this.parse(NotionCommentsReply);
|
|
25
|
+
const markdown = joinMarkdown(args.markdown);
|
|
26
|
+
const result = await notionCommentsCreate({ body: replyBodyFromMarkdown(args.discussionId, markdown) });
|
|
27
|
+
this.setNext(null);
|
|
28
|
+
if (flags.raw)
|
|
29
|
+
return this.setRaw(result.raw);
|
|
30
|
+
return shapeReceipt(result, { action: 'replied_to_discussion', body: markdown, discussionId: args.discussionId });
|
|
31
|
+
}
|
|
32
|
+
render(data) {
|
|
33
|
+
const receipt = data;
|
|
34
|
+
return renderKv(kvEntries({
|
|
35
|
+
created: receipt.created,
|
|
36
|
+
discussion_id: receipt.discussion_id,
|
|
37
|
+
action: receipt.action,
|
|
38
|
+
author: formatAuthor({ author: receipt.author, author_kind: receipt.author_kind }),
|
|
39
|
+
created_time: formatCreated(receipt.created_time),
|
|
40
|
+
warning: receipt.warning,
|
|
41
|
+
body: receipt.body,
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=reply.js.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import { renderKv } from '../../../io/render-kv.js';
|
|
3
|
+
import { renderLabeled } from '../../../io/render-labeled.js';
|
|
4
|
+
import { notionCommentsList } from '../../../services/notion.js';
|
|
5
|
+
import { formatAuthor, formatCreated, formatTarget, notionCommentErrors, NotionCommentsCommand, parseTarget, renderContinuation, shapeThread, } from '../../../lib/notion/comments/shared.js';
|
|
6
|
+
const MAX_THREAD_PAGES = 25;
|
|
7
|
+
export default class NotionCommentsThread extends NotionCommentsCommand {
|
|
8
|
+
static errors = notionCommentErrors;
|
|
9
|
+
static summary = 'Show one Notion discussion thread';
|
|
10
|
+
static description = 'Show comments in one discussion thread for a page or block. Not paginated by CLI contract; use comments list --next for broader scanning.';
|
|
11
|
+
static examples = [
|
|
12
|
+
'<%= config.bin %> notion comments thread 362e1f8b-0d45-4d7b-bd85-cfb7c26f00dc disc_6a91',
|
|
13
|
+
'<%= config.bin %> notion comments thread 362e1f8b-0d45-4d7b-bd85-cfb7c26f00dc disc_6a91 --raw',
|
|
14
|
+
];
|
|
15
|
+
static aliases = [];
|
|
16
|
+
static flags = {
|
|
17
|
+
raw: Flags.boolean({ description: 'Emit the verbatim Notion response body.', default: false }),
|
|
18
|
+
};
|
|
19
|
+
static args = {
|
|
20
|
+
target: Args.string({ description: 'Page or block ID/URL.', required: true }),
|
|
21
|
+
discussionId: Args.string({ description: 'Discussion ID.', required: true }),
|
|
22
|
+
};
|
|
23
|
+
static enableJsonFlag = true;
|
|
24
|
+
static strict = true;
|
|
25
|
+
async execute() {
|
|
26
|
+
const { args, flags } = await this.parse(NotionCommentsThread);
|
|
27
|
+
const target = parseTarget(args.target);
|
|
28
|
+
// Page until we find at least one comment for this discussion or pages
|
|
29
|
+
// are exhausted. Without this, a discussion that lives past the first
|
|
30
|
+
// page produces a false notion.not_found despite existing in Notion.
|
|
31
|
+
let cursor;
|
|
32
|
+
const aggregated = [];
|
|
33
|
+
let pageIndex = 0;
|
|
34
|
+
let raw;
|
|
35
|
+
let lastResult;
|
|
36
|
+
while (pageIndex < MAX_THREAD_PAGES) {
|
|
37
|
+
const result = await notionCommentsList(target.id, cursor);
|
|
38
|
+
lastResult = result;
|
|
39
|
+
raw = result.raw;
|
|
40
|
+
const data = result.data;
|
|
41
|
+
if (Array.isArray(data?.results))
|
|
42
|
+
aggregated.push(...data.results);
|
|
43
|
+
const hasMore = data?.has_more === true && typeof data.next_cursor === 'string' && data.next_cursor.length > 0;
|
|
44
|
+
// Stop early if we have already found at least one comment for the
|
|
45
|
+
// requested discussion — that's enough for shapeThread to succeed and
|
|
46
|
+
// saves further round-trips.
|
|
47
|
+
const matched = aggregated.some((comment) => {
|
|
48
|
+
const record = comment;
|
|
49
|
+
return record !== null && typeof record === 'object' && record.discussion_id === args.discussionId;
|
|
50
|
+
});
|
|
51
|
+
if (matched || !hasMore)
|
|
52
|
+
break;
|
|
53
|
+
cursor = data.next_cursor;
|
|
54
|
+
pageIndex += 1;
|
|
55
|
+
}
|
|
56
|
+
this.setNext(null);
|
|
57
|
+
if (flags.raw)
|
|
58
|
+
return this.setRaw(raw);
|
|
59
|
+
const merged = {
|
|
60
|
+
data: { results: aggregated },
|
|
61
|
+
raw,
|
|
62
|
+
meta: lastResult?.meta ?? { next: null, notion_version: '2026-03-11' },
|
|
63
|
+
};
|
|
64
|
+
return shapeThread(merged, target, args.discussionId);
|
|
65
|
+
}
|
|
66
|
+
render(data) {
|
|
67
|
+
const thread = data;
|
|
68
|
+
const summary = {
|
|
69
|
+
discussion_id: thread.discussion_id,
|
|
70
|
+
target: thread.target ? formatTarget(thread.target) : undefined,
|
|
71
|
+
shown: thread.summary.shown,
|
|
72
|
+
bot_replied: thread.summary.bot_replied,
|
|
73
|
+
last_author: thread.summary.last_author,
|
|
74
|
+
next_action: thread.summary.next_action,
|
|
75
|
+
partial_thread: thread.summary.partial_thread,
|
|
76
|
+
total_seen_for_thread: thread.summary.total_seen_for_thread,
|
|
77
|
+
};
|
|
78
|
+
const header = renderKv(Object.entries(summary).filter(([, value]) => value !== undefined));
|
|
79
|
+
const body = renderLabeled(thread.comments.map((comment) => ({
|
|
80
|
+
label: `${comment.id} · ${formatAuthor(comment)} · ${formatCreated(comment.created)}`,
|
|
81
|
+
body: comment.body ?? comment.snippet,
|
|
82
|
+
})));
|
|
83
|
+
const trailer = renderContinuation(null);
|
|
84
|
+
return [header, body, trailer].filter(Boolean).join('\n\n');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=thread.js.map
|