@andrzejchm/notion-cli 0.6.0 → 0.8.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 +11 -0
- package/dist/cli.js +512 -168
- package/dist/cli.js.map +1 -1
- package/docs/FEATURE-PARITY.md +186 -0
- package/docs/README.agents.md +59 -0
- package/docs/skills/using-notion-cli/SKILL.md +63 -22
- package/docs/testing-setup.md +250 -0
- package/package.json +5 -3
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { readFileSync } from "fs";
|
|
5
5
|
import { dirname, join as join3 } from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
|
-
import { Command as
|
|
7
|
+
import { Command as Command23 } from "commander";
|
|
8
8
|
|
|
9
9
|
// src/commands/append.ts
|
|
10
10
|
import { Command } from "commander";
|
|
@@ -598,6 +598,30 @@ function buildContentRange(content) {
|
|
|
598
598
|
}
|
|
599
599
|
return content;
|
|
600
600
|
}
|
|
601
|
+
async function searchAndReplace(client, pageId, updates, options) {
|
|
602
|
+
await client.pages.updateMarkdown({
|
|
603
|
+
page_id: pageId,
|
|
604
|
+
type: "update_content",
|
|
605
|
+
update_content: {
|
|
606
|
+
content_updates: updates.map((u) => ({
|
|
607
|
+
old_str: u.oldStr,
|
|
608
|
+
new_str: u.newStr,
|
|
609
|
+
...options?.replaceAll && { replace_all_matches: true }
|
|
610
|
+
})),
|
|
611
|
+
...options?.allowDeletingContent && { allow_deleting_content: true }
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
async function replacePageContent(client, pageId, newContent, options) {
|
|
616
|
+
await client.pages.updateMarkdown({
|
|
617
|
+
page_id: pageId,
|
|
618
|
+
type: "replace_content",
|
|
619
|
+
replace_content: {
|
|
620
|
+
new_str: newContent,
|
|
621
|
+
...options?.allowDeletingContent && { allow_deleting_content: true }
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
}
|
|
601
625
|
async function replaceMarkdown(client, pageId, newMarkdown, options) {
|
|
602
626
|
const current = await client.pages.retrieveMarkdown({ page_id: pageId });
|
|
603
627
|
const currentContent = current.markdown.trim();
|
|
@@ -635,7 +659,22 @@ async function replaceMarkdown(client, pageId, newMarkdown, options) {
|
|
|
635
659
|
}
|
|
636
660
|
});
|
|
637
661
|
}
|
|
638
|
-
|
|
662
|
+
function buildIconCover(options) {
|
|
663
|
+
const result = {};
|
|
664
|
+
if (options?.icon) {
|
|
665
|
+
const isUrl = /^https?:\/\//i.test(options.icon);
|
|
666
|
+
if (isUrl) {
|
|
667
|
+
result.icon = { type: "external", external: { url: options.icon } };
|
|
668
|
+
} else {
|
|
669
|
+
result.icon = { type: "emoji", emoji: options.icon };
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
if (options?.cover) {
|
|
673
|
+
result.cover = { type: "external", external: { url: options.cover } };
|
|
674
|
+
}
|
|
675
|
+
return result;
|
|
676
|
+
}
|
|
677
|
+
async function createPage(client, parentId, title, markdown, options) {
|
|
639
678
|
const response = await client.pages.create({
|
|
640
679
|
parent: { type: "page_id", page_id: parentId },
|
|
641
680
|
properties: {
|
|
@@ -643,7 +682,24 @@ async function createPage(client, parentId, title, markdown) {
|
|
|
643
682
|
title: [{ type: "text", text: { content: title, link: null } }]
|
|
644
683
|
}
|
|
645
684
|
},
|
|
646
|
-
...markdown.trim() ? { markdown } : {}
|
|
685
|
+
...markdown.trim() ? { markdown } : {},
|
|
686
|
+
...buildIconCover(options)
|
|
687
|
+
});
|
|
688
|
+
const url = "url" in response ? response.url : response.id;
|
|
689
|
+
return url;
|
|
690
|
+
}
|
|
691
|
+
async function createPageInDatabase(client, databaseId, titlePropName, title, extraProperties, markdown, options) {
|
|
692
|
+
const properties = {
|
|
693
|
+
...extraProperties,
|
|
694
|
+
[titlePropName]: {
|
|
695
|
+
title: [{ type: "text", text: { content: title, link: null } }]
|
|
696
|
+
}
|
|
697
|
+
};
|
|
698
|
+
const response = await client.pages.create({
|
|
699
|
+
parent: { type: "database_id", database_id: databaseId },
|
|
700
|
+
properties,
|
|
701
|
+
...markdown.trim() ? { markdown } : {},
|
|
702
|
+
...buildIconCover(options)
|
|
647
703
|
});
|
|
648
704
|
const url = "url" in response ? response.url : response.id;
|
|
649
705
|
return url;
|
|
@@ -713,6 +769,102 @@ function appendCommand() {
|
|
|
713
769
|
return cmd;
|
|
714
770
|
}
|
|
715
771
|
|
|
772
|
+
// src/commands/archive.ts
|
|
773
|
+
import { Command as Command2 } from "commander";
|
|
774
|
+
|
|
775
|
+
// src/output/format.ts
|
|
776
|
+
var _mode = "auto";
|
|
777
|
+
function setOutputMode(mode) {
|
|
778
|
+
_mode = mode;
|
|
779
|
+
}
|
|
780
|
+
function getOutputMode() {
|
|
781
|
+
return _mode;
|
|
782
|
+
}
|
|
783
|
+
function isatty() {
|
|
784
|
+
return Boolean(process.stdout.isTTY);
|
|
785
|
+
}
|
|
786
|
+
function isHumanMode() {
|
|
787
|
+
if (_mode === "json") return false;
|
|
788
|
+
if (_mode === "md") return false;
|
|
789
|
+
return true;
|
|
790
|
+
}
|
|
791
|
+
function formatJSON(data) {
|
|
792
|
+
return JSON.stringify(data, null, 2);
|
|
793
|
+
}
|
|
794
|
+
var COLUMN_CAPS = {
|
|
795
|
+
TITLE: 50,
|
|
796
|
+
ID: 36
|
|
797
|
+
};
|
|
798
|
+
var DEFAULT_MAX_COL_WIDTH = 40;
|
|
799
|
+
function getColumnCap(header) {
|
|
800
|
+
return COLUMN_CAPS[header.toUpperCase()] ?? DEFAULT_MAX_COL_WIDTH;
|
|
801
|
+
}
|
|
802
|
+
function truncate(str, maxLen) {
|
|
803
|
+
if (str.length <= maxLen) return str;
|
|
804
|
+
return `${str.slice(0, maxLen - 1)}\u2026`;
|
|
805
|
+
}
|
|
806
|
+
function formatTable(rows, headers) {
|
|
807
|
+
const colWidths = headers.map((header, colIdx) => {
|
|
808
|
+
const cap = getColumnCap(header);
|
|
809
|
+
const headerLen = header.length;
|
|
810
|
+
const maxRowLen = rows.reduce((max, row) => {
|
|
811
|
+
const cell = row[colIdx] ?? "";
|
|
812
|
+
return Math.max(max, cell.length);
|
|
813
|
+
}, 0);
|
|
814
|
+
return Math.min(Math.max(headerLen, maxRowLen), cap);
|
|
815
|
+
});
|
|
816
|
+
const sep = "\u2500";
|
|
817
|
+
const colSep = " ";
|
|
818
|
+
const headerRow = headers.map((h, i) => h.padEnd(colWidths[i])).join(colSep);
|
|
819
|
+
const separatorRow = colWidths.map((w) => sep.repeat(w)).join(colSep);
|
|
820
|
+
const dataRows = rows.map(
|
|
821
|
+
(row) => headers.map((_, i) => {
|
|
822
|
+
const cell = row[i] ?? "";
|
|
823
|
+
return truncate(cell, colWidths[i]).padEnd(colWidths[i]);
|
|
824
|
+
}).join(colSep)
|
|
825
|
+
);
|
|
826
|
+
return [headerRow, separatorRow, ...dataRows].join("\n");
|
|
827
|
+
}
|
|
828
|
+
function printOutput(data, tableHeaders, tableRows) {
|
|
829
|
+
const mode = getOutputMode();
|
|
830
|
+
if (mode === "json") {
|
|
831
|
+
process.stdout.write(`${formatJSON(data)}
|
|
832
|
+
`);
|
|
833
|
+
} else if (isHumanMode() && tableHeaders && tableRows) {
|
|
834
|
+
printWithPager(`${formatTable(tableRows, tableHeaders)}
|
|
835
|
+
`);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
function printWithPager(text) {
|
|
839
|
+
process.stdout.write(text);
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// src/commands/archive.ts
|
|
843
|
+
function archiveCommand() {
|
|
844
|
+
const cmd = new Command2("archive");
|
|
845
|
+
cmd.description("archive (trash) a Notion page").argument("<id/url>", "Notion page ID or URL").action(
|
|
846
|
+
withErrorHandling(async (idOrUrl) => {
|
|
847
|
+
const { token, source } = await resolveToken();
|
|
848
|
+
reportTokenSource(source);
|
|
849
|
+
const client = createNotionClient(token);
|
|
850
|
+
const id = parseNotionId(idOrUrl);
|
|
851
|
+
const uuid = toUuid(id);
|
|
852
|
+
const updatedPage = await client.pages.update({
|
|
853
|
+
page_id: uuid,
|
|
854
|
+
archived: true
|
|
855
|
+
});
|
|
856
|
+
const mode = getOutputMode();
|
|
857
|
+
if (mode === "json") {
|
|
858
|
+
process.stdout.write(`${formatJSON(updatedPage)}
|
|
859
|
+
`);
|
|
860
|
+
} else {
|
|
861
|
+
process.stdout.write("Page archived.\n");
|
|
862
|
+
}
|
|
863
|
+
})
|
|
864
|
+
);
|
|
865
|
+
return cmd;
|
|
866
|
+
}
|
|
867
|
+
|
|
716
868
|
// src/commands/auth/index.ts
|
|
717
869
|
function authDefaultAction(authCmd2) {
|
|
718
870
|
return async () => {
|
|
@@ -722,7 +874,7 @@ function authDefaultAction(authCmd2) {
|
|
|
722
874
|
|
|
723
875
|
// src/commands/auth/login.ts
|
|
724
876
|
import { input } from "@inquirer/prompts";
|
|
725
|
-
import { Command as
|
|
877
|
+
import { Command as Command3 } from "commander";
|
|
726
878
|
|
|
727
879
|
// src/oauth/oauth-flow.ts
|
|
728
880
|
import { spawn } from "child_process";
|
|
@@ -969,7 +1121,7 @@ Waiting for callback (up to 120 seconds)...
|
|
|
969
1121
|
|
|
970
1122
|
// src/commands/auth/login.ts
|
|
971
1123
|
function loginCommand() {
|
|
972
|
-
const cmd = new
|
|
1124
|
+
const cmd = new Command3("login");
|
|
973
1125
|
cmd.description("authenticate with Notion via OAuth").option("--profile <name>", "profile name to store credentials in").option(
|
|
974
1126
|
"--manual",
|
|
975
1127
|
"print auth URL instead of opening browser (for headless OAuth)"
|
|
@@ -1032,7 +1184,7 @@ function loginCommand() {
|
|
|
1032
1184
|
|
|
1033
1185
|
// src/commands/auth/logout.ts
|
|
1034
1186
|
import { select } from "@inquirer/prompts";
|
|
1035
|
-
import { Command as
|
|
1187
|
+
import { Command as Command4 } from "commander";
|
|
1036
1188
|
function profileLabel(name, profile) {
|
|
1037
1189
|
const parts = [];
|
|
1038
1190
|
if (profile.oauth_access_token)
|
|
@@ -1045,7 +1197,7 @@ function profileLabel(name, profile) {
|
|
|
1045
1197
|
return `${bold(name)} ${dim(authDesc)}${workspace}`;
|
|
1046
1198
|
}
|
|
1047
1199
|
function logoutCommand() {
|
|
1048
|
-
const cmd = new
|
|
1200
|
+
const cmd = new Command4("logout");
|
|
1049
1201
|
cmd.description("remove a profile and its credentials").option(
|
|
1050
1202
|
"--profile <name>",
|
|
1051
1203
|
"profile name to remove (skips interactive selector)"
|
|
@@ -1103,9 +1255,9 @@ function logoutCommand() {
|
|
|
1103
1255
|
}
|
|
1104
1256
|
|
|
1105
1257
|
// src/commands/auth/status.ts
|
|
1106
|
-
import { Command as
|
|
1258
|
+
import { Command as Command5 } from "commander";
|
|
1107
1259
|
function statusCommand() {
|
|
1108
|
-
const cmd = new
|
|
1260
|
+
const cmd = new Command5("status");
|
|
1109
1261
|
cmd.description("show authentication status for the active profile").option("--profile <name>", "profile name to check").action(
|
|
1110
1262
|
withErrorHandling(async (opts) => {
|
|
1111
1263
|
let profileName = opts.profile;
|
|
@@ -1163,9 +1315,9 @@ function statusCommand() {
|
|
|
1163
1315
|
}
|
|
1164
1316
|
|
|
1165
1317
|
// src/commands/comment-add.ts
|
|
1166
|
-
import { Command as
|
|
1318
|
+
import { Command as Command6 } from "commander";
|
|
1167
1319
|
function commentAddCommand() {
|
|
1168
|
-
const cmd = new
|
|
1320
|
+
const cmd = new Command6("comment");
|
|
1169
1321
|
cmd.description("add a comment to a Notion page").argument("<id/url>", "Notion page ID or URL").requiredOption("-m, --message <text>", "comment text to post").action(
|
|
1170
1322
|
withErrorHandling(async (idOrUrl, opts) => {
|
|
1171
1323
|
const { token, source } = await resolveToken();
|
|
@@ -1183,74 +1335,7 @@ function commentAddCommand() {
|
|
|
1183
1335
|
}
|
|
1184
1336
|
|
|
1185
1337
|
// src/commands/comments.ts
|
|
1186
|
-
import { Command as
|
|
1187
|
-
|
|
1188
|
-
// src/output/format.ts
|
|
1189
|
-
var _mode = "auto";
|
|
1190
|
-
function setOutputMode(mode) {
|
|
1191
|
-
_mode = mode;
|
|
1192
|
-
}
|
|
1193
|
-
function getOutputMode() {
|
|
1194
|
-
return _mode;
|
|
1195
|
-
}
|
|
1196
|
-
function isatty() {
|
|
1197
|
-
return Boolean(process.stdout.isTTY);
|
|
1198
|
-
}
|
|
1199
|
-
function isHumanMode() {
|
|
1200
|
-
if (_mode === "json") return false;
|
|
1201
|
-
if (_mode === "md") return false;
|
|
1202
|
-
return true;
|
|
1203
|
-
}
|
|
1204
|
-
function formatJSON(data) {
|
|
1205
|
-
return JSON.stringify(data, null, 2);
|
|
1206
|
-
}
|
|
1207
|
-
var COLUMN_CAPS = {
|
|
1208
|
-
TITLE: 50,
|
|
1209
|
-
ID: 36
|
|
1210
|
-
};
|
|
1211
|
-
var DEFAULT_MAX_COL_WIDTH = 40;
|
|
1212
|
-
function getColumnCap(header) {
|
|
1213
|
-
return COLUMN_CAPS[header.toUpperCase()] ?? DEFAULT_MAX_COL_WIDTH;
|
|
1214
|
-
}
|
|
1215
|
-
function truncate(str, maxLen) {
|
|
1216
|
-
if (str.length <= maxLen) return str;
|
|
1217
|
-
return `${str.slice(0, maxLen - 1)}\u2026`;
|
|
1218
|
-
}
|
|
1219
|
-
function formatTable(rows, headers) {
|
|
1220
|
-
const colWidths = headers.map((header, colIdx) => {
|
|
1221
|
-
const cap = getColumnCap(header);
|
|
1222
|
-
const headerLen = header.length;
|
|
1223
|
-
const maxRowLen = rows.reduce((max, row) => {
|
|
1224
|
-
const cell = row[colIdx] ?? "";
|
|
1225
|
-
return Math.max(max, cell.length);
|
|
1226
|
-
}, 0);
|
|
1227
|
-
return Math.min(Math.max(headerLen, maxRowLen), cap);
|
|
1228
|
-
});
|
|
1229
|
-
const sep = "\u2500";
|
|
1230
|
-
const colSep = " ";
|
|
1231
|
-
const headerRow = headers.map((h, i) => h.padEnd(colWidths[i])).join(colSep);
|
|
1232
|
-
const separatorRow = colWidths.map((w) => sep.repeat(w)).join(colSep);
|
|
1233
|
-
const dataRows = rows.map(
|
|
1234
|
-
(row) => headers.map((_, i) => {
|
|
1235
|
-
const cell = row[i] ?? "";
|
|
1236
|
-
return truncate(cell, colWidths[i]).padEnd(colWidths[i]);
|
|
1237
|
-
}).join(colSep)
|
|
1238
|
-
);
|
|
1239
|
-
return [headerRow, separatorRow, ...dataRows].join("\n");
|
|
1240
|
-
}
|
|
1241
|
-
function printOutput(data, tableHeaders, tableRows) {
|
|
1242
|
-
const mode = getOutputMode();
|
|
1243
|
-
if (mode === "json") {
|
|
1244
|
-
process.stdout.write(`${formatJSON(data)}
|
|
1245
|
-
`);
|
|
1246
|
-
} else if (isHumanMode() && tableHeaders && tableRows) {
|
|
1247
|
-
printWithPager(`${formatTable(tableRows, tableHeaders)}
|
|
1248
|
-
`);
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
|
-
function printWithPager(text) {
|
|
1252
|
-
process.stdout.write(text);
|
|
1253
|
-
}
|
|
1338
|
+
import { Command as Command7 } from "commander";
|
|
1254
1339
|
|
|
1255
1340
|
// src/output/paginate.ts
|
|
1256
1341
|
async function paginateResults(fetcher) {
|
|
@@ -1268,7 +1353,7 @@ async function paginateResults(fetcher) {
|
|
|
1268
1353
|
|
|
1269
1354
|
// src/commands/comments.ts
|
|
1270
1355
|
function commentsCommand() {
|
|
1271
|
-
const cmd = new
|
|
1356
|
+
const cmd = new Command7("comments");
|
|
1272
1357
|
cmd.description("list comments on a Notion page").argument("<id/url>", "Notion page ID or URL").option("--json", "output as JSON").action(
|
|
1273
1358
|
withErrorHandling(async (idOrUrl, opts) => {
|
|
1274
1359
|
if (opts.json) setOutputMode("json");
|
|
@@ -1299,7 +1384,7 @@ function commentsCommand() {
|
|
|
1299
1384
|
}
|
|
1300
1385
|
|
|
1301
1386
|
// src/commands/completion.ts
|
|
1302
|
-
import { Command as
|
|
1387
|
+
import { Command as Command8 } from "commander";
|
|
1303
1388
|
var BASH_COMPLETION = `# notion bash completion
|
|
1304
1389
|
_notion_completion() {
|
|
1305
1390
|
local cur prev words cword
|
|
@@ -1401,7 +1486,7 @@ complete -c notion -n '__fish_seen_subcommand_from completion' -a zsh -d 'zsh co
|
|
|
1401
1486
|
complete -c notion -n '__fish_seen_subcommand_from completion' -a fish -d 'fish completion script'
|
|
1402
1487
|
`;
|
|
1403
1488
|
function completionCommand() {
|
|
1404
|
-
const cmd = new
|
|
1489
|
+
const cmd = new Command8("completion");
|
|
1405
1490
|
cmd.description("output shell completion script").argument("<shell>", "shell type (bash, zsh, fish)").action(
|
|
1406
1491
|
withErrorHandling(async (shell) => {
|
|
1407
1492
|
switch (shell) {
|
|
@@ -1427,40 +1512,6 @@ function completionCommand() {
|
|
|
1427
1512
|
}
|
|
1428
1513
|
|
|
1429
1514
|
// src/commands/create-page.ts
|
|
1430
|
-
import { Command as Command8 } from "commander";
|
|
1431
|
-
function createPageCommand() {
|
|
1432
|
-
const cmd = new Command8("create-page");
|
|
1433
|
-
cmd.description("create a new Notion page under a parent page").requiredOption("--parent <id/url>", "parent page ID or URL").requiredOption("--title <title>", "page title").option(
|
|
1434
|
-
"-m, --message <markdown>",
|
|
1435
|
-
"inline markdown content for the page body"
|
|
1436
|
-
).action(
|
|
1437
|
-
withErrorHandling(
|
|
1438
|
-
async (opts) => {
|
|
1439
|
-
const { token, source } = await resolveToken();
|
|
1440
|
-
reportTokenSource(source);
|
|
1441
|
-
const client = createNotionClient(token);
|
|
1442
|
-
let markdown = "";
|
|
1443
|
-
if (opts.message) {
|
|
1444
|
-
markdown = opts.message;
|
|
1445
|
-
} else if (!process.stdin.isTTY) {
|
|
1446
|
-
markdown = await readStdin();
|
|
1447
|
-
}
|
|
1448
|
-
const parentUuid = toUuid(parseNotionId(opts.parent));
|
|
1449
|
-
const url = await createPage(
|
|
1450
|
-
client,
|
|
1451
|
-
parentUuid,
|
|
1452
|
-
opts.title,
|
|
1453
|
-
markdown
|
|
1454
|
-
);
|
|
1455
|
-
process.stdout.write(`${url}
|
|
1456
|
-
`);
|
|
1457
|
-
}
|
|
1458
|
-
)
|
|
1459
|
-
);
|
|
1460
|
-
return cmd;
|
|
1461
|
-
}
|
|
1462
|
-
|
|
1463
|
-
// src/commands/db/query.ts
|
|
1464
1515
|
import { Command as Command9 } from "commander";
|
|
1465
1516
|
|
|
1466
1517
|
// src/services/database.service.ts
|
|
@@ -1486,7 +1537,8 @@ async function fetchDatabaseSchema(client, dbId) {
|
|
|
1486
1537
|
properties[name] = config;
|
|
1487
1538
|
}
|
|
1488
1539
|
}
|
|
1489
|
-
|
|
1540
|
+
const databaseId = "parent" in ds && ds.parent && typeof ds.parent === "object" && "database_id" in ds.parent ? ds.parent.database_id : dbId;
|
|
1541
|
+
return { id: dbId, databaseId, title, properties };
|
|
1490
1542
|
}
|
|
1491
1543
|
async function queryDatabase(client, dbId, opts = {}) {
|
|
1492
1544
|
const rawPages = await paginateResults(
|
|
@@ -1627,7 +1679,202 @@ function displayPropertyValue(prop) {
|
|
|
1627
1679
|
}
|
|
1628
1680
|
}
|
|
1629
1681
|
|
|
1682
|
+
// src/services/update.service.ts
|
|
1683
|
+
var UNSUPPORTED_TYPES = /* @__PURE__ */ new Set([
|
|
1684
|
+
"relation",
|
|
1685
|
+
"formula",
|
|
1686
|
+
"rollup",
|
|
1687
|
+
"created_time",
|
|
1688
|
+
"created_by",
|
|
1689
|
+
"last_edited_time",
|
|
1690
|
+
"last_edited_by",
|
|
1691
|
+
"files",
|
|
1692
|
+
"unique_id",
|
|
1693
|
+
"verification",
|
|
1694
|
+
"button"
|
|
1695
|
+
]);
|
|
1696
|
+
function buildPropertyUpdate(propName, propType, value) {
|
|
1697
|
+
if (UNSUPPORTED_TYPES.has(propType)) {
|
|
1698
|
+
throw new CliError(
|
|
1699
|
+
ErrorCodes.INVALID_ARG,
|
|
1700
|
+
`Property "${propName}" has type "${propType}" which cannot be set via the CLI.`,
|
|
1701
|
+
"Supported types: title, rich_text, select, status, multi_select, number, checkbox, url, email, phone_number, date"
|
|
1702
|
+
);
|
|
1703
|
+
}
|
|
1704
|
+
if (value === "") {
|
|
1705
|
+
return null;
|
|
1706
|
+
}
|
|
1707
|
+
switch (propType) {
|
|
1708
|
+
case "title":
|
|
1709
|
+
return { title: [{ type: "text", text: { content: value } }] };
|
|
1710
|
+
case "rich_text":
|
|
1711
|
+
return { rich_text: [{ type: "text", text: { content: value } }] };
|
|
1712
|
+
case "select":
|
|
1713
|
+
return { select: { name: value } };
|
|
1714
|
+
case "status":
|
|
1715
|
+
return { status: { name: value } };
|
|
1716
|
+
case "multi_select":
|
|
1717
|
+
return {
|
|
1718
|
+
multi_select: value.split(",").map((v) => v.trim()).filter(Boolean).map((v) => ({ name: v }))
|
|
1719
|
+
};
|
|
1720
|
+
case "number": {
|
|
1721
|
+
const n = Number(value);
|
|
1722
|
+
if (Number.isNaN(n)) {
|
|
1723
|
+
throw new CliError(
|
|
1724
|
+
ErrorCodes.INVALID_ARG,
|
|
1725
|
+
`Invalid number value "${value}" for property "${propName}".`,
|
|
1726
|
+
'Provide a numeric value, e.g. --prop "Count=42"'
|
|
1727
|
+
);
|
|
1728
|
+
}
|
|
1729
|
+
return { number: n };
|
|
1730
|
+
}
|
|
1731
|
+
case "checkbox": {
|
|
1732
|
+
const lower = value.toLowerCase();
|
|
1733
|
+
return { checkbox: lower === "true" || lower === "yes" };
|
|
1734
|
+
}
|
|
1735
|
+
case "url":
|
|
1736
|
+
return { url: value };
|
|
1737
|
+
case "email":
|
|
1738
|
+
return { email: value };
|
|
1739
|
+
case "phone_number":
|
|
1740
|
+
return { phone_number: value };
|
|
1741
|
+
case "date": {
|
|
1742
|
+
const parts = value.split(",");
|
|
1743
|
+
const start = parts[0].trim();
|
|
1744
|
+
const end = parts[1]?.trim();
|
|
1745
|
+
return { date: end ? { start, end } : { start } };
|
|
1746
|
+
}
|
|
1747
|
+
default:
|
|
1748
|
+
throw new CliError(
|
|
1749
|
+
ErrorCodes.INVALID_ARG,
|
|
1750
|
+
`Property "${propName}" has unsupported type "${propType}".`,
|
|
1751
|
+
"Supported types: title, rich_text, select, status, multi_select, number, checkbox, url, email, phone_number, date"
|
|
1752
|
+
);
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
function buildPropertiesPayload(propStrings, schemaOrPage) {
|
|
1756
|
+
const isPage = "object" in schemaOrPage && schemaOrPage.object === "page";
|
|
1757
|
+
const schema = isPage ? schemaOrPage.properties : schemaOrPage;
|
|
1758
|
+
const result = {};
|
|
1759
|
+
for (const propString of propStrings) {
|
|
1760
|
+
const eqIdx = propString.indexOf("=");
|
|
1761
|
+
if (eqIdx === -1) {
|
|
1762
|
+
throw new CliError(
|
|
1763
|
+
ErrorCodes.INVALID_ARG,
|
|
1764
|
+
`Invalid --prop value: "${propString}". Expected format: "PropertyName=Value".`,
|
|
1765
|
+
'Example: --prop "Status=Done"'
|
|
1766
|
+
);
|
|
1767
|
+
}
|
|
1768
|
+
const propName = propString.slice(0, eqIdx).trim();
|
|
1769
|
+
const value = propString.slice(eqIdx + 1);
|
|
1770
|
+
const schemaProp = schema[propName];
|
|
1771
|
+
if (!schemaProp) {
|
|
1772
|
+
const available = Object.keys(schema).join(", ");
|
|
1773
|
+
throw new CliError(
|
|
1774
|
+
ErrorCodes.INVALID_ARG,
|
|
1775
|
+
`Property "${propName}" not found on this page.`,
|
|
1776
|
+
`Available properties: ${available}`
|
|
1777
|
+
);
|
|
1778
|
+
}
|
|
1779
|
+
const propType = schemaProp.type;
|
|
1780
|
+
const payload = buildPropertyUpdate(propName, propType, value);
|
|
1781
|
+
result[propName] = payload;
|
|
1782
|
+
}
|
|
1783
|
+
return result;
|
|
1784
|
+
}
|
|
1785
|
+
async function updatePageProperties(client, pageId, properties) {
|
|
1786
|
+
const response = await client.pages.update({
|
|
1787
|
+
page_id: pageId,
|
|
1788
|
+
properties
|
|
1789
|
+
});
|
|
1790
|
+
return response;
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
// src/commands/create-page.ts
|
|
1794
|
+
function collectProps(val, acc) {
|
|
1795
|
+
acc.push(val);
|
|
1796
|
+
return acc;
|
|
1797
|
+
}
|
|
1798
|
+
async function tryGetDatabaseSchema(client, uuid) {
|
|
1799
|
+
try {
|
|
1800
|
+
return await fetchDatabaseSchema(client, uuid);
|
|
1801
|
+
} catch {
|
|
1802
|
+
return null;
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
function createPageCommand() {
|
|
1806
|
+
const cmd = new Command9("create-page");
|
|
1807
|
+
cmd.description("create a new Notion page under a parent page or database").requiredOption("--parent <id/url>", "parent page or database ID/URL").requiredOption("--title <title>", "page title").option(
|
|
1808
|
+
"-m, --message <markdown>",
|
|
1809
|
+
"inline markdown content for the page body"
|
|
1810
|
+
).option(
|
|
1811
|
+
"--prop <property=value>",
|
|
1812
|
+
"set a property value (repeatable, database parents only)",
|
|
1813
|
+
collectProps,
|
|
1814
|
+
[]
|
|
1815
|
+
).option("--icon <emoji-or-url>", "page icon \u2014 emoji character or image URL").option("--cover <url>", "page cover image URL").action(
|
|
1816
|
+
withErrorHandling(async (opts) => {
|
|
1817
|
+
const { token, source } = await resolveToken();
|
|
1818
|
+
reportTokenSource(source);
|
|
1819
|
+
const client = createNotionClient(token);
|
|
1820
|
+
let markdown = "";
|
|
1821
|
+
if (opts.message) {
|
|
1822
|
+
markdown = opts.message;
|
|
1823
|
+
} else if (!process.stdin.isTTY) {
|
|
1824
|
+
markdown = await readStdin();
|
|
1825
|
+
}
|
|
1826
|
+
const parentUuid = toUuid(parseNotionId(opts.parent));
|
|
1827
|
+
const iconCover = { icon: opts.icon, cover: opts.cover };
|
|
1828
|
+
const dbSchema = await tryGetDatabaseSchema(client, parentUuid);
|
|
1829
|
+
if (dbSchema) {
|
|
1830
|
+
const titleEntry = Object.entries(dbSchema.properties).find(
|
|
1831
|
+
([, prop]) => prop.type === "title"
|
|
1832
|
+
);
|
|
1833
|
+
if (!titleEntry) {
|
|
1834
|
+
throw new CliError(
|
|
1835
|
+
ErrorCodes.API_ERROR,
|
|
1836
|
+
"Database has no title property.",
|
|
1837
|
+
"This database cannot accept new pages."
|
|
1838
|
+
);
|
|
1839
|
+
}
|
|
1840
|
+
const [titlePropName] = titleEntry;
|
|
1841
|
+
const extraProperties = opts.prop.length > 0 ? buildPropertiesPayload(opts.prop, dbSchema.properties) : {};
|
|
1842
|
+
const url = await createPageInDatabase(
|
|
1843
|
+
client,
|
|
1844
|
+
dbSchema.databaseId,
|
|
1845
|
+
titlePropName,
|
|
1846
|
+
opts.title,
|
|
1847
|
+
extraProperties,
|
|
1848
|
+
markdown,
|
|
1849
|
+
iconCover
|
|
1850
|
+
);
|
|
1851
|
+
process.stdout.write(`${url}
|
|
1852
|
+
`);
|
|
1853
|
+
} else {
|
|
1854
|
+
if (opts.prop.length > 0) {
|
|
1855
|
+
throw new CliError(
|
|
1856
|
+
ErrorCodes.INVALID_ARG,
|
|
1857
|
+
"--prop is only supported when the parent is a database.",
|
|
1858
|
+
"To set properties, use a database ID/URL as --parent"
|
|
1859
|
+
);
|
|
1860
|
+
}
|
|
1861
|
+
const url = await createPage(
|
|
1862
|
+
client,
|
|
1863
|
+
parentUuid,
|
|
1864
|
+
opts.title,
|
|
1865
|
+
markdown,
|
|
1866
|
+
iconCover
|
|
1867
|
+
);
|
|
1868
|
+
process.stdout.write(`${url}
|
|
1869
|
+
`);
|
|
1870
|
+
}
|
|
1871
|
+
})
|
|
1872
|
+
);
|
|
1873
|
+
return cmd;
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1630
1876
|
// src/commands/db/query.ts
|
|
1877
|
+
import { Command as Command10 } from "commander";
|
|
1631
1878
|
var SKIP_TYPES_IN_AUTO = /* @__PURE__ */ new Set(["relation", "rich_text", "people"]);
|
|
1632
1879
|
function autoSelectColumns(schema, entries) {
|
|
1633
1880
|
const termWidth = process.stdout.columns || 120;
|
|
@@ -1655,7 +1902,7 @@ function autoSelectColumns(schema, entries) {
|
|
|
1655
1902
|
return selected;
|
|
1656
1903
|
}
|
|
1657
1904
|
function dbQueryCommand() {
|
|
1658
|
-
return new
|
|
1905
|
+
return new Command10("query").description("Query database entries with optional filtering and sorting").argument("<id>", "Notion database ID or URL").option(
|
|
1659
1906
|
"--filter <filter>",
|
|
1660
1907
|
'Filter entries (repeatable): --filter "Status=Done"',
|
|
1661
1908
|
collect,
|
|
@@ -1710,9 +1957,9 @@ function collect(value, previous) {
|
|
|
1710
1957
|
}
|
|
1711
1958
|
|
|
1712
1959
|
// src/commands/db/schema.ts
|
|
1713
|
-
import { Command as
|
|
1960
|
+
import { Command as Command11 } from "commander";
|
|
1714
1961
|
function dbSchemaCommand() {
|
|
1715
|
-
return new
|
|
1962
|
+
return new Command11("schema").description("Show database schema (property names, types, and options)").argument("<id>", "Notion database ID or URL").option("--json", "Output raw JSON").action(
|
|
1716
1963
|
withErrorHandling(async (id, options) => {
|
|
1717
1964
|
const { token } = await resolveToken();
|
|
1718
1965
|
const client = createNotionClient(token);
|
|
@@ -1736,29 +1983,59 @@ function dbSchemaCommand() {
|
|
|
1736
1983
|
}
|
|
1737
1984
|
|
|
1738
1985
|
// src/commands/edit-page.ts
|
|
1739
|
-
import { Command as
|
|
1986
|
+
import { Command as Command12 } from "commander";
|
|
1987
|
+
function collect2(val, acc) {
|
|
1988
|
+
acc.push(val);
|
|
1989
|
+
return acc;
|
|
1990
|
+
}
|
|
1740
1991
|
function editPageCommand() {
|
|
1741
|
-
const cmd = new
|
|
1992
|
+
const cmd = new Command12("edit-page");
|
|
1742
1993
|
cmd.description(
|
|
1743
1994
|
"replace a Notion page's content \u2014 full page or a targeted section"
|
|
1744
1995
|
).argument("<id/url>", "Notion page ID or URL").option(
|
|
1745
1996
|
"-m, --message <markdown>",
|
|
1746
1997
|
"new markdown content for the page body"
|
|
1747
1998
|
).option(
|
|
1999
|
+
"--find <text>",
|
|
2000
|
+
"text to find (repeatable, pair with --replace)",
|
|
2001
|
+
collect2,
|
|
2002
|
+
[]
|
|
2003
|
+
).option(
|
|
2004
|
+
"--replace <text>",
|
|
2005
|
+
"replacement text (repeatable, pair with --find)",
|
|
2006
|
+
collect2,
|
|
2007
|
+
[]
|
|
2008
|
+
).option("--all", "replace all matches of each --find pattern").option(
|
|
1748
2009
|
"--range <selector>",
|
|
1749
|
-
'ellipsis selector to replace only a section, e.g. "## My Section...last line"'
|
|
2010
|
+
'[deprecated] ellipsis selector to replace only a section, e.g. "## My Section...last line"'
|
|
1750
2011
|
).option(
|
|
1751
2012
|
"--allow-deleting-content",
|
|
1752
|
-
"allow deletion
|
|
2013
|
+
"allow deletion of child pages/databases"
|
|
1753
2014
|
).action(
|
|
1754
2015
|
withErrorHandling(async (idOrUrl, opts) => {
|
|
1755
2016
|
const { token, source } = await resolveToken();
|
|
1756
2017
|
reportTokenSource(source);
|
|
1757
2018
|
const client = createNotionClient(token);
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
)
|
|
2019
|
+
const pageId = parseNotionId(idOrUrl);
|
|
2020
|
+
const uuid = toUuid(pageId);
|
|
2021
|
+
if (opts.find.length > 0) {
|
|
2022
|
+
if (opts.find.length !== opts.replace.length) {
|
|
2023
|
+
throw new CliError(
|
|
2024
|
+
ErrorCodes.INVALID_ARG,
|
|
2025
|
+
`Mismatched --find/--replace: got ${opts.find.length} --find and ${opts.replace.length} --replace flags.`,
|
|
2026
|
+
"Provide the same number of --find and --replace flags, paired by position."
|
|
2027
|
+
);
|
|
2028
|
+
}
|
|
2029
|
+
const updates = opts.find.map((oldStr, i) => ({
|
|
2030
|
+
oldStr,
|
|
2031
|
+
newStr: opts.replace[i]
|
|
2032
|
+
}));
|
|
2033
|
+
await searchAndReplace(client, uuid, updates, {
|
|
2034
|
+
replaceAll: opts.all ?? false,
|
|
2035
|
+
allowDeletingContent: opts.allowDeletingContent ?? false
|
|
2036
|
+
});
|
|
2037
|
+
process.stdout.write("Page content updated.\n");
|
|
2038
|
+
return;
|
|
1762
2039
|
}
|
|
1763
2040
|
let markdown = "";
|
|
1764
2041
|
if (opts.message) {
|
|
@@ -1769,38 +2046,39 @@ function editPageCommand() {
|
|
|
1769
2046
|
throw new CliError(
|
|
1770
2047
|
ErrorCodes.INVALID_ARG,
|
|
1771
2048
|
"No content provided (stdin was empty).",
|
|
1772
|
-
"Pass
|
|
2049
|
+
"Pass content via -m/--message for full replacement, --find/--replace for targeted edits, or pipe content through stdin"
|
|
1773
2050
|
);
|
|
1774
2051
|
}
|
|
1775
2052
|
} else {
|
|
1776
2053
|
throw new CliError(
|
|
1777
2054
|
ErrorCodes.INVALID_ARG,
|
|
1778
2055
|
"No content provided.",
|
|
1779
|
-
"Pass
|
|
2056
|
+
"Pass content via -m/--message for full replacement, --find/--replace for targeted edits, or pipe content through stdin"
|
|
1780
2057
|
);
|
|
1781
2058
|
}
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
try {
|
|
1785
|
-
if (opts.range) {
|
|
2059
|
+
if (opts.range) {
|
|
2060
|
+
try {
|
|
1786
2061
|
await replaceMarkdown(client, uuid, markdown, {
|
|
1787
2062
|
range: opts.range,
|
|
1788
2063
|
allowDeletingContent: opts.allowDeletingContent ?? false
|
|
1789
2064
|
});
|
|
1790
|
-
}
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
);
|
|
2065
|
+
} catch (error2) {
|
|
2066
|
+
if (isNotionValidationError(error2)) {
|
|
2067
|
+
throw new CliError(
|
|
2068
|
+
ErrorCodes.INVALID_ARG,
|
|
2069
|
+
`Selector not found: "${opts.range}". ${error2.message}`,
|
|
2070
|
+
SELECTOR_HINT,
|
|
2071
|
+
error2
|
|
2072
|
+
);
|
|
2073
|
+
}
|
|
2074
|
+
throw error2;
|
|
1801
2075
|
}
|
|
1802
|
-
|
|
2076
|
+
process.stdout.write("Page content replaced.\n");
|
|
2077
|
+
return;
|
|
1803
2078
|
}
|
|
2079
|
+
await replacePageContent(client, uuid, markdown, {
|
|
2080
|
+
allowDeletingContent: opts.allowDeletingContent ?? false
|
|
2081
|
+
});
|
|
1804
2082
|
process.stdout.write("Page content replaced.\n");
|
|
1805
2083
|
})
|
|
1806
2084
|
);
|
|
@@ -1809,7 +2087,7 @@ function editPageCommand() {
|
|
|
1809
2087
|
|
|
1810
2088
|
// src/commands/init.ts
|
|
1811
2089
|
import { confirm, input as input2, password } from "@inquirer/prompts";
|
|
1812
|
-
import { Command as
|
|
2090
|
+
import { Command as Command13 } from "commander";
|
|
1813
2091
|
async function runInitFlow() {
|
|
1814
2092
|
const profileName = await input2({
|
|
1815
2093
|
message: "Profile name:",
|
|
@@ -1889,7 +2167,7 @@ async function runInitFlow() {
|
|
|
1889
2167
|
stderrWrite(dim(" notion auth login"));
|
|
1890
2168
|
}
|
|
1891
2169
|
function initCommand() {
|
|
1892
|
-
const cmd = new
|
|
2170
|
+
const cmd = new Command13("init");
|
|
1893
2171
|
cmd.description("authenticate with Notion and save a profile").action(
|
|
1894
2172
|
withErrorHandling(async () => {
|
|
1895
2173
|
if (!process.stdin.isTTY) {
|
|
@@ -1907,7 +2185,7 @@ function initCommand() {
|
|
|
1907
2185
|
|
|
1908
2186
|
// src/commands/ls.ts
|
|
1909
2187
|
import { isFullPageOrDataSource } from "@notionhq/client";
|
|
1910
|
-
import { Command as
|
|
2188
|
+
import { Command as Command14 } from "commander";
|
|
1911
2189
|
function getTitle(item) {
|
|
1912
2190
|
if (item.object === "data_source") {
|
|
1913
2191
|
return item.title.map((t) => t.plain_text).join("") || "(untitled)";
|
|
@@ -1924,7 +2202,7 @@ function displayType(item) {
|
|
|
1924
2202
|
return item.object === "data_source" ? "database" : item.object;
|
|
1925
2203
|
}
|
|
1926
2204
|
function lsCommand() {
|
|
1927
|
-
const cmd = new
|
|
2205
|
+
const cmd = new Command14("ls");
|
|
1928
2206
|
cmd.description("list accessible Notion pages and databases").option(
|
|
1929
2207
|
"--type <type>",
|
|
1930
2208
|
"filter by object type (page or database)",
|
|
@@ -1987,10 +2265,10 @@ function lsCommand() {
|
|
|
1987
2265
|
// src/commands/open.ts
|
|
1988
2266
|
import { exec } from "child_process";
|
|
1989
2267
|
import { promisify } from "util";
|
|
1990
|
-
import { Command as
|
|
2268
|
+
import { Command as Command15 } from "commander";
|
|
1991
2269
|
var execAsync = promisify(exec);
|
|
1992
2270
|
function openCommand() {
|
|
1993
|
-
const cmd = new
|
|
2271
|
+
const cmd = new Command15("open");
|
|
1994
2272
|
cmd.description("open a Notion page in the default browser").argument("<id/url>", "Notion page ID or URL").action(
|
|
1995
2273
|
withErrorHandling(async (idOrUrl) => {
|
|
1996
2274
|
const id = parseNotionId(idOrUrl);
|
|
@@ -2006,9 +2284,9 @@ function openCommand() {
|
|
|
2006
2284
|
}
|
|
2007
2285
|
|
|
2008
2286
|
// src/commands/profile/list.ts
|
|
2009
|
-
import { Command as
|
|
2287
|
+
import { Command as Command16 } from "commander";
|
|
2010
2288
|
function profileListCommand() {
|
|
2011
|
-
const cmd = new
|
|
2289
|
+
const cmd = new Command16("list");
|
|
2012
2290
|
cmd.description("list all authentication profiles").action(
|
|
2013
2291
|
withErrorHandling(async () => {
|
|
2014
2292
|
const config = await readGlobalConfig();
|
|
@@ -2037,9 +2315,9 @@ function profileListCommand() {
|
|
|
2037
2315
|
}
|
|
2038
2316
|
|
|
2039
2317
|
// src/commands/profile/remove.ts
|
|
2040
|
-
import { Command as
|
|
2318
|
+
import { Command as Command17 } from "commander";
|
|
2041
2319
|
function profileRemoveCommand() {
|
|
2042
|
-
const cmd = new
|
|
2320
|
+
const cmd = new Command17("remove");
|
|
2043
2321
|
cmd.description("remove an authentication profile").argument("<name>", "profile name to remove").action(
|
|
2044
2322
|
withErrorHandling(async (name) => {
|
|
2045
2323
|
const config = await readGlobalConfig();
|
|
@@ -2065,9 +2343,9 @@ function profileRemoveCommand() {
|
|
|
2065
2343
|
}
|
|
2066
2344
|
|
|
2067
2345
|
// src/commands/profile/use.ts
|
|
2068
|
-
import { Command as
|
|
2346
|
+
import { Command as Command18 } from "commander";
|
|
2069
2347
|
function profileUseCommand() {
|
|
2070
|
-
const cmd = new
|
|
2348
|
+
const cmd = new Command18("use");
|
|
2071
2349
|
cmd.description("switch the active profile").argument("<name>", "profile name to activate").action(
|
|
2072
2350
|
withErrorHandling(async (name) => {
|
|
2073
2351
|
const config = await readGlobalConfig();
|
|
@@ -2090,7 +2368,7 @@ function profileUseCommand() {
|
|
|
2090
2368
|
}
|
|
2091
2369
|
|
|
2092
2370
|
// src/commands/read.ts
|
|
2093
|
-
import { Command as
|
|
2371
|
+
import { Command as Command19 } from "commander";
|
|
2094
2372
|
|
|
2095
2373
|
// src/output/markdown.ts
|
|
2096
2374
|
import { Chalk as Chalk2 } from "chalk";
|
|
@@ -2230,7 +2508,7 @@ async function fetchPageMarkdown(client, pageId) {
|
|
|
2230
2508
|
|
|
2231
2509
|
// src/commands/read.ts
|
|
2232
2510
|
function readCommand() {
|
|
2233
|
-
return new
|
|
2511
|
+
return new Command19("read").description("Read a Notion page as markdown").argument("<id>", "Notion page ID or URL").action(
|
|
2234
2512
|
withErrorHandling(async (id) => {
|
|
2235
2513
|
const { token } = await resolveToken();
|
|
2236
2514
|
const client = createNotionClient(token);
|
|
@@ -2256,7 +2534,7 @@ function readCommand() {
|
|
|
2256
2534
|
|
|
2257
2535
|
// src/commands/search.ts
|
|
2258
2536
|
import { isFullPageOrDataSource as isFullPageOrDataSource2 } from "@notionhq/client";
|
|
2259
|
-
import { Command as
|
|
2537
|
+
import { Command as Command20 } from "commander";
|
|
2260
2538
|
function getTitle2(item) {
|
|
2261
2539
|
if (item.object === "data_source") {
|
|
2262
2540
|
return item.title.map((t) => t.plain_text).join("") || "(untitled)";
|
|
@@ -2276,7 +2554,7 @@ function displayType2(item) {
|
|
|
2276
2554
|
return item.object === "data_source" ? "database" : item.object;
|
|
2277
2555
|
}
|
|
2278
2556
|
function searchCommand() {
|
|
2279
|
-
const cmd = new
|
|
2557
|
+
const cmd = new Command20("search");
|
|
2280
2558
|
cmd.description("search Notion workspace by keyword").argument("<query>", "search keyword").option(
|
|
2281
2559
|
"--type <type>",
|
|
2282
2560
|
"filter by object type (page or database)",
|
|
@@ -2333,8 +2611,72 @@ function searchCommand() {
|
|
|
2333
2611
|
return cmd;
|
|
2334
2612
|
}
|
|
2335
2613
|
|
|
2614
|
+
// src/commands/update.ts
|
|
2615
|
+
import { Command as Command21 } from "commander";
|
|
2616
|
+
function collectProps2(val, acc) {
|
|
2617
|
+
acc.push(val);
|
|
2618
|
+
return acc;
|
|
2619
|
+
}
|
|
2620
|
+
function updateCommand() {
|
|
2621
|
+
const cmd = new Command21("update");
|
|
2622
|
+
cmd.description("update properties on a Notion page").argument("<id/url>", "Notion page ID or URL").option(
|
|
2623
|
+
"--prop <property=value>",
|
|
2624
|
+
"set a property value (repeatable)",
|
|
2625
|
+
collectProps2,
|
|
2626
|
+
[]
|
|
2627
|
+
).option("--title <title>", "set the page title").action(
|
|
2628
|
+
withErrorHandling(async (idOrUrl, opts) => {
|
|
2629
|
+
if (opts.title === void 0 && opts.prop.length === 0) {
|
|
2630
|
+
throw new CliError(
|
|
2631
|
+
ErrorCodes.INVALID_ARG,
|
|
2632
|
+
"No properties to update.",
|
|
2633
|
+
'Provide at least one --prop "Name=Value" or --title "New Title"'
|
|
2634
|
+
);
|
|
2635
|
+
}
|
|
2636
|
+
const { token, source } = await resolveToken();
|
|
2637
|
+
reportTokenSource(source);
|
|
2638
|
+
const client = createNotionClient(token);
|
|
2639
|
+
const id = parseNotionId(idOrUrl);
|
|
2640
|
+
const uuid = toUuid(id);
|
|
2641
|
+
const page = await client.pages.retrieve({
|
|
2642
|
+
page_id: uuid
|
|
2643
|
+
});
|
|
2644
|
+
const properties = buildPropertiesPayload(opts.prop, page);
|
|
2645
|
+
if (opts.title !== void 0) {
|
|
2646
|
+
const titleEntry = Object.entries(page.properties).find(
|
|
2647
|
+
([, prop]) => prop.type === "title"
|
|
2648
|
+
);
|
|
2649
|
+
if (!titleEntry) {
|
|
2650
|
+
throw new CliError(
|
|
2651
|
+
ErrorCodes.INVALID_ARG,
|
|
2652
|
+
"This page has no title property.",
|
|
2653
|
+
"Use --prop to set properties by name instead"
|
|
2654
|
+
);
|
|
2655
|
+
}
|
|
2656
|
+
const [titlePropName] = titleEntry;
|
|
2657
|
+
properties[titlePropName] = {
|
|
2658
|
+
title: [{ type: "text", text: { content: opts.title } }]
|
|
2659
|
+
};
|
|
2660
|
+
}
|
|
2661
|
+
const updatedPage = await updatePageProperties(
|
|
2662
|
+
client,
|
|
2663
|
+
uuid,
|
|
2664
|
+
properties
|
|
2665
|
+
);
|
|
2666
|
+
const mode = getOutputMode();
|
|
2667
|
+
if (mode === "json") {
|
|
2668
|
+
process.stdout.write(`${formatJSON(updatedPage)}
|
|
2669
|
+
`);
|
|
2670
|
+
} else {
|
|
2671
|
+
process.stdout.write("Page updated.\n");
|
|
2672
|
+
}
|
|
2673
|
+
})
|
|
2674
|
+
);
|
|
2675
|
+
return cmd;
|
|
2676
|
+
}
|
|
2677
|
+
|
|
2336
2678
|
// src/commands/users.ts
|
|
2337
|
-
import { Command as
|
|
2679
|
+
import { Command as Command22 } from "commander";
|
|
2338
2680
|
function getEmailOrWorkspace(user) {
|
|
2339
2681
|
if (user.type === "person") {
|
|
2340
2682
|
return user.person.email ?? "\u2014";
|
|
@@ -2346,7 +2688,7 @@ function getEmailOrWorkspace(user) {
|
|
|
2346
2688
|
return "\u2014";
|
|
2347
2689
|
}
|
|
2348
2690
|
function usersCommand() {
|
|
2349
|
-
const cmd = new
|
|
2691
|
+
const cmd = new Command22("users");
|
|
2350
2692
|
cmd.description("list all users in the workspace").option("--json", "output as JSON").action(
|
|
2351
2693
|
withErrorHandling(async (opts) => {
|
|
2352
2694
|
if (opts.json) setOutputMode("json");
|
|
@@ -2377,7 +2719,7 @@ var __dirname = dirname(__filename);
|
|
|
2377
2719
|
var pkg = JSON.parse(
|
|
2378
2720
|
readFileSync(join3(__dirname, "../package.json"), "utf-8")
|
|
2379
2721
|
);
|
|
2380
|
-
var program = new
|
|
2722
|
+
var program = new Command23();
|
|
2381
2723
|
program.name("notion").description("Notion CLI \u2014 read Notion pages and databases from the terminal").version(pkg.version);
|
|
2382
2724
|
program.option("--verbose", "show API requests/responses").option("--color", "force color output").option("--json", "force JSON output (overrides TTY detection)").option("--md", "force markdown output for page content");
|
|
2383
2725
|
program.configureOutput({
|
|
@@ -2398,7 +2740,7 @@ program.hook("preAction", (thisCommand) => {
|
|
|
2398
2740
|
setOutputMode("md");
|
|
2399
2741
|
}
|
|
2400
2742
|
});
|
|
2401
|
-
var authCmd = new
|
|
2743
|
+
var authCmd = new Command23("auth").description("manage Notion authentication");
|
|
2402
2744
|
authCmd.action(authDefaultAction(authCmd));
|
|
2403
2745
|
authCmd.addCommand(loginCommand());
|
|
2404
2746
|
authCmd.addCommand(logoutCommand());
|
|
@@ -2408,7 +2750,7 @@ authCmd.addCommand(profileUseCommand());
|
|
|
2408
2750
|
authCmd.addCommand(profileRemoveCommand());
|
|
2409
2751
|
program.addCommand(authCmd);
|
|
2410
2752
|
program.addCommand(initCommand(), { hidden: true });
|
|
2411
|
-
var profileCmd = new
|
|
2753
|
+
var profileCmd = new Command23("profile").description(
|
|
2412
2754
|
"manage authentication profiles"
|
|
2413
2755
|
);
|
|
2414
2756
|
profileCmd.addCommand(profileListCommand());
|
|
@@ -2425,7 +2767,9 @@ program.addCommand(commentAddCommand());
|
|
|
2425
2767
|
program.addCommand(appendCommand());
|
|
2426
2768
|
program.addCommand(createPageCommand());
|
|
2427
2769
|
program.addCommand(editPageCommand());
|
|
2428
|
-
|
|
2770
|
+
program.addCommand(updateCommand());
|
|
2771
|
+
program.addCommand(archiveCommand());
|
|
2772
|
+
var dbCmd = new Command23("db").description("Database operations");
|
|
2429
2773
|
dbCmd.addCommand(dbSchemaCommand());
|
|
2430
2774
|
dbCmd.addCommand(dbQueryCommand());
|
|
2431
2775
|
program.addCommand(dbCmd);
|