safe-notion 0.1.3 → 0.2.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 CHANGED
@@ -28,7 +28,7 @@ export NOTION_TOKEN="your-notion-integration-token"
28
28
  設定ファイルを初期化:
29
29
 
30
30
  ```bash
31
- notion-safe config init
31
+ safe-notion config init
32
32
  ```
33
33
 
34
34
  設定ファイルの場所: `~/.config/safe-notion/config.jsonc`
@@ -85,34 +85,34 @@ notion-safe config init
85
85
  ### ページ操作
86
86
 
87
87
  ```bash
88
- notion-safe page get <page-id>
89
- notion-safe page create --parent <parent-id> --title "タイトル"
90
- notion-safe page update <page-id>
88
+ safe-notion page get <page-id>
89
+ safe-notion page create --parent <parent-id> --title "タイトル"
90
+ safe-notion page update <page-id>
91
91
  ```
92
92
 
93
93
  ### データベース操作
94
94
 
95
95
  ```bash
96
- notion-safe db get <database-id>
97
- notion-safe db query <database-id>
98
- notion-safe db create-page <database-id>
96
+ safe-notion db get <database-id>
97
+ safe-notion db query <database-id>
98
+ safe-notion db create-page <database-id>
99
99
  ```
100
100
 
101
101
  ### ブロック操作
102
102
 
103
103
  ```bash
104
- notion-safe block get <block-id>
105
- notion-safe block children <block-id>
106
- notion-safe block append <block-id> --children '<json>'
107
- notion-safe block delete <block-id>
104
+ safe-notion block get <block-id>
105
+ safe-notion block children <block-id>
106
+ safe-notion block append <block-id> --children '<json>'
107
+ safe-notion block delete <block-id>
108
108
  ```
109
109
 
110
110
  ### 設定管理
111
111
 
112
112
  ```bash
113
- notion-safe config init # 設定ファイルを初期化
114
- notion-safe config validate # 設定を検証
115
- notion-safe config path # 設定ファイルのパスを表示
113
+ safe-notion config init # 設定ファイルを初期化
114
+ safe-notion config validate # 設定を検証
115
+ safe-notion config path # 設定ファイルのパスを表示
116
116
  ```
117
117
 
118
118
  ## 開発
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function createSearchCommand(): Command;
package/dist/index.js CHANGED
@@ -2,13 +2,14 @@
2
2
  // @bun
3
3
 
4
4
  // src/index.ts
5
- import { Command as Command5 } from "commander";
5
+ import { Command as Command6 } from "commander";
6
6
 
7
7
  // src/commands/page.ts
8
8
  import { Command } from "commander";
9
9
 
10
10
  // src/notion-client.ts
11
11
  import { Client } from "@notionhq/client";
12
+ import { LogLevel } from "@notionhq/client/build/src/logging";
12
13
 
13
14
  // src/config.ts
14
15
  import { readFileSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
@@ -356,12 +357,16 @@ function clearCache() {
356
357
  }
357
358
 
358
359
  // src/notion-client.ts
360
+ var debugMode = false;
359
361
  class NotionSafeClient {
360
362
  client;
361
363
  config;
362
364
  constructor() {
363
365
  const token = getNotionToken();
364
- this.client = new Client({ auth: token });
366
+ this.client = new Client({
367
+ auth: token,
368
+ logLevel: debugMode ? LogLevel.WARN : LogLevel.ERROR
369
+ });
365
370
  this.config = loadConfig();
366
371
  }
367
372
  async ensurePermission(resourceId, operation, pageIdForCondition) {
@@ -400,8 +405,18 @@ class NotionSafeClient {
400
405
  }
401
406
  async queryDatabase(databaseId, params) {
402
407
  await this.ensurePermission(databaseId, "database:query");
403
- return this.client.databases.query({
404
- database_id: databaseId,
408
+ const database = await this.client.databases.retrieve({
409
+ database_id: databaseId
410
+ });
411
+ const dataSourceId = database.data_sources?.[0]?.id;
412
+ if (!dataSourceId) {
413
+ throw {
414
+ error: "Database has no data source",
415
+ code: "NO_DATA_SOURCE"
416
+ };
417
+ }
418
+ return this.client.dataSources.query({
419
+ data_source_id: dataSourceId,
405
420
  ...params
406
421
  });
407
422
  }
@@ -435,6 +450,17 @@ class NotionSafeClient {
435
450
  await this.ensurePermission(blockId, "block:delete");
436
451
  return this.client.blocks.delete({ block_id: blockId });
437
452
  }
453
+ async search(params) {
454
+ const hasReadPermission = this.config.defaultPermission === "read" || this.config.rules.some((rule) => rule.permissions.some((p) => ["page:read", "database:read", "block:read"].includes(p)));
455
+ if (!hasReadPermission) {
456
+ const error = {
457
+ error: "Search not allowed: no read permissions configured",
458
+ code: "PERMISSION_DENIED"
459
+ };
460
+ throw error;
461
+ }
462
+ return this.client.search(params);
463
+ }
438
464
  clearCache() {
439
465
  clearCache();
440
466
  }
@@ -680,11 +706,75 @@ function createConfigCommand() {
680
706
  return config;
681
707
  }
682
708
 
709
+ // src/commands/search.ts
710
+ import { Command as Command5 } from "commander";
711
+ function createSearchCommand() {
712
+ const search = new Command5("search").description("Search pages and databases");
713
+ search.argument("[query]", "Search query string").option("--filter <type>", "Filter by object type: page or database").option("--sort <direction>", "Sort by last_edited_time: ascending or descending").option("--start-cursor <cursor>", "Pagination cursor").option("--page-size <size>", "Number of results per page", "100").action(async (query, options) => {
714
+ try {
715
+ const client = getClient();
716
+ const params = {};
717
+ if (query) {
718
+ params.query = query;
719
+ }
720
+ if (options.filter) {
721
+ const filterValue = options.filter === "database" ? "data_source" : options.filter;
722
+ if (filterValue !== "page" && filterValue !== "data_source") {
723
+ throw {
724
+ error: "Invalid filter value. Must be 'page' or 'database'",
725
+ code: "INVALID_ARGUMENT"
726
+ };
727
+ }
728
+ params.filter = { property: "object", value: filterValue };
729
+ }
730
+ if (options.sort) {
731
+ if (options.sort !== "ascending" && options.sort !== "descending") {
732
+ throw {
733
+ error: "Invalid sort value. Must be 'ascending' or 'descending'",
734
+ code: "INVALID_ARGUMENT"
735
+ };
736
+ }
737
+ params.sort = {
738
+ direction: options.sort,
739
+ timestamp: "last_edited_time"
740
+ };
741
+ }
742
+ if (options.startCursor) {
743
+ params.start_cursor = options.startCursor;
744
+ }
745
+ if (options.pageSize) {
746
+ params.page_size = parseInt(options.pageSize, 10);
747
+ }
748
+ const result = await client.search(params);
749
+ outputJson(result);
750
+ } catch (error) {
751
+ handleError(error);
752
+ }
753
+ });
754
+ return search;
755
+ }
756
+
757
+ // src/notion-client.ts
758
+ import { Client as Client2 } from "@notionhq/client";
759
+ import { LogLevel as LogLevel2 } from "@notionhq/client/build/src/logging";
760
+ var debugMode2 = false;
761
+ function setDebugMode(enabled) {
762
+ debugMode2 = enabled;
763
+ clientInstance2 = null;
764
+ }
765
+ var clientInstance2 = null;
766
+
683
767
  // src/index.ts
684
- var program = new Command5;
685
- program.name("notion-safe").description("A safe Notion API wrapper CLI for AI agents").version("0.1.0");
768
+ var program = new Command6;
769
+ program.name("notion-safe").description("A safe Notion API wrapper CLI for AI agents").version("0.1.0").option("--debug", "Enable debug output including API warnings").hook("preAction", () => {
770
+ const opts = program.opts();
771
+ if (opts.debug) {
772
+ setDebugMode(true);
773
+ }
774
+ });
686
775
  program.addCommand(createPageCommand());
687
776
  program.addCommand(createDbCommand());
688
777
  program.addCommand(createBlockCommand());
689
778
  program.addCommand(createConfigCommand());
779
+ program.addCommand(createSearchCommand());
690
780
  program.parse();
@@ -1,4 +1,5 @@
1
1
  import { Client } from "@notionhq/client";
2
+ export declare function setDebugMode(enabled: boolean): void;
2
3
  type CreatePageParams = Parameters<Client["pages"]["create"]>[0];
3
4
  type UpdatePageParams = Parameters<Client["pages"]["update"]>[0];
4
5
  type AppendBlockChildrenParams = Parameters<Client["blocks"]["children"]["append"]>[0];
@@ -23,6 +24,19 @@ export declare class NotionSafeClient {
23
24
  getBlockChildren(blockId: string, startCursor?: string, pageSize?: number): Promise<unknown>;
24
25
  appendBlockChildren(blockId: string, children: AppendBlockChildrenParams["children"]): Promise<unknown>;
25
26
  deleteBlock(blockId: string): Promise<unknown>;
27
+ search(params: {
28
+ query?: string;
29
+ filter?: {
30
+ property: "object";
31
+ value: "page" | "data_source";
32
+ };
33
+ sort?: {
34
+ direction: "ascending" | "descending";
35
+ timestamp: "last_edited_time";
36
+ };
37
+ start_cursor?: string;
38
+ page_size?: number;
39
+ }): Promise<unknown>;
26
40
  clearCache(): void;
27
41
  }
28
42
  export declare function getClient(): NotionSafeClient;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "safe-notion",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "A safe Notion API wrapper CLI for AI agents with granular permission control",
5
5
  "license": "MIT",
6
6
  "author": "shoppingjaws",