@catafal/notion-cli 5.9.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/LICENSE +21 -0
- package/README.md +552 -0
- package/bin/dev +17 -0
- package/bin/dev.cmd +3 -0
- package/bin/run +14 -0
- package/bin/run.cmd +3 -0
- package/dist/base-command.d.ts +73 -0
- package/dist/base-command.js +179 -0
- package/dist/base-flags.d.ts +14 -0
- package/dist/base-flags.js +59 -0
- package/dist/cache.d.ts +84 -0
- package/dist/cache.js +351 -0
- package/dist/commands/append.d.ts +37 -0
- package/dist/commands/append.js +120 -0
- package/dist/commands/batch/delete.d.ts +42 -0
- package/dist/commands/batch/delete.js +199 -0
- package/dist/commands/batch/retrieve.d.ts +43 -0
- package/dist/commands/batch/retrieve.js +272 -0
- package/dist/commands/block/append.d.ts +42 -0
- package/dist/commands/block/append.js +219 -0
- package/dist/commands/block/delete.d.ts +30 -0
- package/dist/commands/block/delete.js +97 -0
- package/dist/commands/block/retrieve/children.d.ts +31 -0
- package/dist/commands/block/retrieve/children.js +177 -0
- package/dist/commands/block/retrieve.d.ts +30 -0
- package/dist/commands/block/retrieve.js +101 -0
- package/dist/commands/block/update.d.ts +45 -0
- package/dist/commands/block/update.js +242 -0
- package/dist/commands/bookmark/list.d.ts +30 -0
- package/dist/commands/bookmark/list.js +60 -0
- package/dist/commands/bookmark/remove.d.ts +26 -0
- package/dist/commands/bookmark/remove.js +47 -0
- package/dist/commands/bookmark/set.d.ts +29 -0
- package/dist/commands/bookmark/set.js +96 -0
- package/dist/commands/browse.d.ts +13 -0
- package/dist/commands/browse.js +44 -0
- package/dist/commands/cache/info.d.ts +19 -0
- package/dist/commands/cache/info.js +145 -0
- package/dist/commands/config/set-token.d.ts +22 -0
- package/dist/commands/config/set-token.js +137 -0
- package/dist/commands/daily/index.d.ts +32 -0
- package/dist/commands/daily/index.js +135 -0
- package/dist/commands/daily/setup.d.ts +42 -0
- package/dist/commands/daily/setup.js +149 -0
- package/dist/commands/db/create.d.ts +31 -0
- package/dist/commands/db/create.js +124 -0
- package/dist/commands/db/query.d.ts +41 -0
- package/dist/commands/db/query.js +360 -0
- package/dist/commands/db/retrieve.d.ts +33 -0
- package/dist/commands/db/retrieve.js +134 -0
- package/dist/commands/db/schema.d.ts +32 -0
- package/dist/commands/db/schema.js +308 -0
- package/dist/commands/db/update.d.ts +31 -0
- package/dist/commands/db/update.js +117 -0
- package/dist/commands/doctor.d.ts +50 -0
- package/dist/commands/doctor.js +420 -0
- package/dist/commands/init.d.ts +65 -0
- package/dist/commands/init.js +479 -0
- package/dist/commands/list.d.ts +29 -0
- package/dist/commands/list.js +219 -0
- package/dist/commands/open.d.ts +29 -0
- package/dist/commands/open.js +100 -0
- package/dist/commands/page/create.d.ts +33 -0
- package/dist/commands/page/create.js +261 -0
- package/dist/commands/page/delete.d.ts +36 -0
- package/dist/commands/page/delete.js +107 -0
- package/dist/commands/page/export.d.ts +38 -0
- package/dist/commands/page/export.js +120 -0
- package/dist/commands/page/retrieve/property_item.d.ts +24 -0
- package/dist/commands/page/retrieve/property_item.js +75 -0
- package/dist/commands/page/retrieve.d.ts +36 -0
- package/dist/commands/page/retrieve.js +244 -0
- package/dist/commands/page/update.d.ts +34 -0
- package/dist/commands/page/update.js +184 -0
- package/dist/commands/quick.d.ts +35 -0
- package/dist/commands/quick.js +168 -0
- package/dist/commands/search.d.ts +43 -0
- package/dist/commands/search.js +361 -0
- package/dist/commands/stats.d.ts +35 -0
- package/dist/commands/stats.js +274 -0
- package/dist/commands/sync.d.ts +24 -0
- package/dist/commands/sync.js +183 -0
- package/dist/commands/template/get.d.ts +28 -0
- package/dist/commands/template/get.js +59 -0
- package/dist/commands/template/list.d.ts +32 -0
- package/dist/commands/template/list.js +62 -0
- package/dist/commands/template/remove.d.ts +27 -0
- package/dist/commands/template/remove.js +48 -0
- package/dist/commands/template/save.d.ts +32 -0
- package/dist/commands/template/save.js +92 -0
- package/dist/commands/template/use.d.ts +34 -0
- package/dist/commands/template/use.js +142 -0
- package/dist/commands/user/list.d.ts +27 -0
- package/dist/commands/user/list.js +99 -0
- package/dist/commands/user/retrieve/bot.d.ts +28 -0
- package/dist/commands/user/retrieve/bot.js +96 -0
- package/dist/commands/user/retrieve.d.ts +30 -0
- package/dist/commands/user/retrieve.js +103 -0
- package/dist/commands/whoami.d.ts +19 -0
- package/dist/commands/whoami.js +175 -0
- package/dist/deduplication.d.ts +41 -0
- package/dist/deduplication.js +71 -0
- package/dist/envelope.d.ts +169 -0
- package/dist/envelope.js +257 -0
- package/dist/errors/enhanced-errors.d.ts +168 -0
- package/dist/errors/enhanced-errors.js +567 -0
- package/dist/errors/index.d.ts +18 -0
- package/dist/errors/index.js +33 -0
- package/dist/examples/cache-retry-examples.d.ts +64 -0
- package/dist/examples/cache-retry-examples.js +375 -0
- package/dist/helper.d.ts +102 -0
- package/dist/helper.js +885 -0
- package/dist/http-agent.d.ts +38 -0
- package/dist/http-agent.js +60 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -0
- package/dist/interface.d.ts +4 -0
- package/dist/interface.js +2 -0
- package/dist/notion.d.ts +144 -0
- package/dist/notion.js +547 -0
- package/dist/retry.d.ts +72 -0
- package/dist/retry.js +381 -0
- package/dist/utils/bookmarks.d.ts +32 -0
- package/dist/utils/bookmarks.js +98 -0
- package/dist/utils/daily-config.d.ts +22 -0
- package/dist/utils/daily-config.js +60 -0
- package/dist/utils/disk-cache.d.ts +80 -0
- package/dist/utils/disk-cache.js +291 -0
- package/dist/utils/fuzzy.d.ts +36 -0
- package/dist/utils/fuzzy.js +69 -0
- package/dist/utils/interactive-navigator.d.ts +63 -0
- package/dist/utils/interactive-navigator.js +123 -0
- package/dist/utils/markdown-to-blocks.d.ts +21 -0
- package/dist/utils/markdown-to-blocks.js +333 -0
- package/dist/utils/notion-resolver.d.ts +49 -0
- package/dist/utils/notion-resolver.js +278 -0
- package/dist/utils/notion-url-parser.d.ts +48 -0
- package/dist/utils/notion-url-parser.js +121 -0
- package/dist/utils/property-expander.d.ts +45 -0
- package/dist/utils/property-expander.js +323 -0
- package/dist/utils/schema-examples.d.ts +40 -0
- package/dist/utils/schema-examples.js +359 -0
- package/dist/utils/schema-extractor.d.ts +65 -0
- package/dist/utils/schema-extractor.js +235 -0
- package/dist/utils/shell-config.d.ts +30 -0
- package/dist/utils/shell-config.js +84 -0
- package/dist/utils/table-formatter.d.ts +36 -0
- package/dist/utils/table-formatter.js +125 -0
- package/dist/utils/templates.d.ts +30 -0
- package/dist/utils/templates.js +82 -0
- package/dist/utils/terminal-banner.d.ts +24 -0
- package/dist/utils/terminal-banner.js +34 -0
- package/dist/utils/token-validator.d.ts +42 -0
- package/dist/utils/token-validator.js +66 -0
- package/dist/utils/update-notifier.d.ts +26 -0
- package/dist/utils/update-notifier.js +54 -0
- package/dist/utils/workspace-cache.d.ts +58 -0
- package/dist/utils/workspace-cache.js +185 -0
- package/oclif.manifest.json +6471 -0
- package/package.json +118 -0
- package/scripts/banner.js +38 -0
- package/scripts/postinstall.js +44 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const table_formatter_1 = require("../../utils/table-formatter");
|
|
5
|
+
const notion = require("../../notion");
|
|
6
|
+
const helper_1 = require("../../helper");
|
|
7
|
+
const base_flags_1 = require("../../base-flags");
|
|
8
|
+
const errors_1 = require("../../errors");
|
|
9
|
+
class UserRetrieve extends core_1.Command {
|
|
10
|
+
async run() {
|
|
11
|
+
const { args, flags } = await this.parse(UserRetrieve);
|
|
12
|
+
try {
|
|
13
|
+
let res = await notion.retrieveUser(args.user_id);
|
|
14
|
+
// Apply minimal flag to strip metadata
|
|
15
|
+
if (flags.minimal) {
|
|
16
|
+
res = (0, helper_1.stripMetadata)(res);
|
|
17
|
+
}
|
|
18
|
+
// Handle JSON output for automation
|
|
19
|
+
if (flags.json) {
|
|
20
|
+
this.log(JSON.stringify({
|
|
21
|
+
success: true,
|
|
22
|
+
data: res,
|
|
23
|
+
timestamp: new Date().toISOString()
|
|
24
|
+
}, null, 2));
|
|
25
|
+
process.exit(0);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
// Handle raw JSON output (legacy)
|
|
29
|
+
if (flags.raw) {
|
|
30
|
+
(0, helper_1.outputRawJson)(res);
|
|
31
|
+
process.exit(0);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// Handle table output
|
|
35
|
+
const columns = {
|
|
36
|
+
id: {},
|
|
37
|
+
name: {},
|
|
38
|
+
object: {},
|
|
39
|
+
type: {},
|
|
40
|
+
person_or_bot: {
|
|
41
|
+
header: 'person/bot',
|
|
42
|
+
get: (row) => {
|
|
43
|
+
if (row.type === 'person') {
|
|
44
|
+
return row.person;
|
|
45
|
+
}
|
|
46
|
+
return row.bot;
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
avatar_url: {},
|
|
50
|
+
};
|
|
51
|
+
const options = {
|
|
52
|
+
printLine: this.log.bind(this),
|
|
53
|
+
...flags,
|
|
54
|
+
};
|
|
55
|
+
(0, table_formatter_1.formatTable)([res], columns, options);
|
|
56
|
+
process.exit(0);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
const cliError = error instanceof errors_1.NotionCLIError
|
|
60
|
+
? error
|
|
61
|
+
: (0, errors_1.wrapNotionError)(error, {
|
|
62
|
+
resourceType: 'user',
|
|
63
|
+
attemptedId: args.user_id,
|
|
64
|
+
endpoint: 'users.retrieve'
|
|
65
|
+
});
|
|
66
|
+
if (flags.json) {
|
|
67
|
+
this.log(JSON.stringify(cliError.toJSON(), null, 2));
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
this.error(cliError.toHumanString());
|
|
71
|
+
}
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
UserRetrieve.description = 'Retrieve a user';
|
|
77
|
+
UserRetrieve.aliases = ['user:r'];
|
|
78
|
+
UserRetrieve.examples = [
|
|
79
|
+
{
|
|
80
|
+
description: 'Retrieve a user',
|
|
81
|
+
command: `$ notion-cli user retrieve USER_ID`,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
description: 'Retrieve a user and output raw json',
|
|
85
|
+
command: `$ notion-cli user retrieve USER_ID -r`,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
description: 'Retrieve a user and output JSON for automation',
|
|
89
|
+
command: `$ notion-cli user retrieve USER_ID --json`,
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
UserRetrieve.args = {
|
|
93
|
+
user_id: core_1.Args.string(),
|
|
94
|
+
};
|
|
95
|
+
UserRetrieve.flags = {
|
|
96
|
+
raw: core_1.Flags.boolean({
|
|
97
|
+
char: 'r',
|
|
98
|
+
description: 'output raw json',
|
|
99
|
+
}),
|
|
100
|
+
...table_formatter_1.tableFlags,
|
|
101
|
+
...base_flags_1.AutomationFlags,
|
|
102
|
+
};
|
|
103
|
+
exports.default = UserRetrieve;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Whoami extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static aliases: string[];
|
|
5
|
+
static examples: {
|
|
6
|
+
description: string;
|
|
7
|
+
command: string;
|
|
8
|
+
}[];
|
|
9
|
+
static flags: {
|
|
10
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
'page-size': import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
|
+
retry: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
timeout: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
14
|
+
'no-cache': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
verbose: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
minimal: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
17
|
+
};
|
|
18
|
+
run(): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const base_flags_1 = require("../base-flags");
|
|
5
|
+
const notion = require("../notion");
|
|
6
|
+
const cache_1 = require("../cache");
|
|
7
|
+
const errors_1 = require("../errors");
|
|
8
|
+
const workspace_cache_1 = require("../utils/workspace-cache");
|
|
9
|
+
const token_validator_1 = require("../utils/token-validator");
|
|
10
|
+
class Whoami extends core_1.Command {
|
|
11
|
+
async run() {
|
|
12
|
+
var _a;
|
|
13
|
+
const { flags } = await this.parse(Whoami);
|
|
14
|
+
const startTime = Date.now();
|
|
15
|
+
try {
|
|
16
|
+
// Verify NOTION_TOKEN is set (throws if not)
|
|
17
|
+
(0, token_validator_1.validateNotionToken)();
|
|
18
|
+
// Get bot user info (with retry and caching)
|
|
19
|
+
const user = await notion.botUser();
|
|
20
|
+
// Get cache stats from in-memory cache
|
|
21
|
+
const cacheStats = cache_1.cacheManager.getStats();
|
|
22
|
+
const cacheHitRate = cache_1.cacheManager.getHitRate();
|
|
23
|
+
// Load workspace cache (databases.json)
|
|
24
|
+
const cache = await (0, workspace_cache_1.loadCache)();
|
|
25
|
+
// Calculate connection latency
|
|
26
|
+
const latencyMs = Date.now() - startTime;
|
|
27
|
+
// Extract bot info safely
|
|
28
|
+
let botInfo = null;
|
|
29
|
+
let workspaceInfo = null;
|
|
30
|
+
if (user.type === 'bot') {
|
|
31
|
+
const botUser = user;
|
|
32
|
+
if (botUser.bot && typeof botUser.bot === 'object' && 'owner' in botUser.bot) {
|
|
33
|
+
botInfo = {
|
|
34
|
+
owner: botUser.bot.owner,
|
|
35
|
+
workspace_name: botUser.bot.workspace_name,
|
|
36
|
+
workspace_id: botUser.bot.workspace_id,
|
|
37
|
+
};
|
|
38
|
+
// Build workspace info if available
|
|
39
|
+
if (botUser.bot.workspace_name) {
|
|
40
|
+
workspaceInfo = {
|
|
41
|
+
name: botUser.bot.workspace_name,
|
|
42
|
+
id: botUser.bot.workspace_id,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Build response data
|
|
48
|
+
const data = {
|
|
49
|
+
bot: {
|
|
50
|
+
id: user.id,
|
|
51
|
+
name: user.name || 'Unnamed Bot',
|
|
52
|
+
type: user.type,
|
|
53
|
+
...(botInfo && { bot_info: botInfo })
|
|
54
|
+
},
|
|
55
|
+
workspace: workspaceInfo,
|
|
56
|
+
api_version: '2022-06-28',
|
|
57
|
+
cli_version: this.config.version,
|
|
58
|
+
cache_status: {
|
|
59
|
+
enabled: !flags['no-cache'] && cache_1.cacheManager.isEnabled(),
|
|
60
|
+
in_memory: {
|
|
61
|
+
size: cacheStats.size,
|
|
62
|
+
hits: cacheStats.hits,
|
|
63
|
+
misses: cacheStats.misses,
|
|
64
|
+
hit_rate: cacheHitRate,
|
|
65
|
+
evictions: cacheStats.evictions,
|
|
66
|
+
},
|
|
67
|
+
workspace: {
|
|
68
|
+
databases_cached: ((_a = cache === null || cache === void 0 ? void 0 : cache.databases) === null || _a === void 0 ? void 0 : _a.length) || 0,
|
|
69
|
+
last_sync: (cache === null || cache === void 0 ? void 0 : cache.lastSync) || null,
|
|
70
|
+
cache_version: (cache === null || cache === void 0 ? void 0 : cache.version) || null,
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
connection: {
|
|
74
|
+
status: 'connected',
|
|
75
|
+
latency_ms: latencyMs
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
// Output JSON envelope
|
|
79
|
+
if (flags.json) {
|
|
80
|
+
this.log(JSON.stringify({
|
|
81
|
+
success: true,
|
|
82
|
+
data,
|
|
83
|
+
metadata: {
|
|
84
|
+
timestamp: new Date().toISOString(),
|
|
85
|
+
command: 'whoami',
|
|
86
|
+
execution_time_ms: latencyMs
|
|
87
|
+
}
|
|
88
|
+
}, null, 2));
|
|
89
|
+
process.exit(0);
|
|
90
|
+
}
|
|
91
|
+
// Human-readable output
|
|
92
|
+
this.log('\nConnection Status');
|
|
93
|
+
this.log('='.repeat(60));
|
|
94
|
+
this.log(`Status: Connected`);
|
|
95
|
+
this.log(`Latency: ${data.connection.latency_ms}ms`);
|
|
96
|
+
this.log('\nBot Information');
|
|
97
|
+
this.log('='.repeat(60));
|
|
98
|
+
this.log(`Name: ${data.bot.name}`);
|
|
99
|
+
this.log(`ID: ${data.bot.id}`);
|
|
100
|
+
this.log(`Type: ${data.bot.type}`);
|
|
101
|
+
if (data.workspace) {
|
|
102
|
+
this.log('\nWorkspace Information');
|
|
103
|
+
this.log('='.repeat(60));
|
|
104
|
+
this.log(`Name: ${data.workspace.name || 'N/A'}`);
|
|
105
|
+
if (data.workspace.id) {
|
|
106
|
+
this.log(`ID: ${data.workspace.id}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
this.log('\nAPI & CLI Version');
|
|
110
|
+
this.log('='.repeat(60));
|
|
111
|
+
this.log(`CLI: ${data.cli_version}`);
|
|
112
|
+
this.log(`API: ${data.api_version}`);
|
|
113
|
+
this.log('\nCache Status');
|
|
114
|
+
this.log('='.repeat(60));
|
|
115
|
+
this.log(`Enabled: ${data.cache_status.enabled ? 'Yes' : 'No'}`);
|
|
116
|
+
if (data.cache_status.enabled) {
|
|
117
|
+
this.log('\nIn-Memory Cache:');
|
|
118
|
+
this.log(` Size: ${data.cache_status.in_memory.size} entries`);
|
|
119
|
+
this.log(` Hits: ${data.cache_status.in_memory.hits}`);
|
|
120
|
+
this.log(` Misses: ${data.cache_status.in_memory.misses}`);
|
|
121
|
+
this.log(` Hit Rate: ${(data.cache_status.in_memory.hit_rate * 100).toFixed(1)}%`);
|
|
122
|
+
this.log(` Evictions: ${data.cache_status.in_memory.evictions}`);
|
|
123
|
+
this.log('\nWorkspace Cache:');
|
|
124
|
+
this.log(` Databases: ${data.cache_status.workspace.databases_cached}`);
|
|
125
|
+
if (data.cache_status.workspace.last_sync) {
|
|
126
|
+
const syncDate = new Date(data.cache_status.workspace.last_sync);
|
|
127
|
+
this.log(` Last Sync: ${syncDate.toLocaleString()}`);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
this.log(` Last Sync: Never (run 'notion-cli sync' to initialize)`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
this.log('\n' + '='.repeat(60));
|
|
134
|
+
this.log('\nConnection verified successfully!');
|
|
135
|
+
// Provide helpful tips
|
|
136
|
+
if (!cache || cache.databases.length === 0) {
|
|
137
|
+
this.log('\nTip: Run "notion-cli sync" to cache workspace databases for faster lookups');
|
|
138
|
+
}
|
|
139
|
+
process.exit(0);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
const cliError = (0, errors_1.wrapNotionError)(error, {
|
|
143
|
+
endpoint: 'users.botUser',
|
|
144
|
+
resourceType: 'user'
|
|
145
|
+
});
|
|
146
|
+
if (flags.json) {
|
|
147
|
+
this.log(JSON.stringify(cliError.toJSON(), null, 2));
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
this.error(cliError.toHumanString());
|
|
151
|
+
}
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
Whoami.description = 'Verify API connectivity and show workspace context';
|
|
157
|
+
Whoami.aliases = ['test', 'health', 'connectivity'];
|
|
158
|
+
Whoami.examples = [
|
|
159
|
+
{
|
|
160
|
+
description: 'Check connection and show bot info',
|
|
161
|
+
command: '$ notion-cli whoami',
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
description: 'Check connection and output as JSON',
|
|
165
|
+
command: '$ notion-cli whoami --json',
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
description: 'Bypass cache for fresh connectivity test',
|
|
169
|
+
command: '$ notion-cli whoami --no-cache',
|
|
170
|
+
},
|
|
171
|
+
];
|
|
172
|
+
Whoami.flags = {
|
|
173
|
+
...base_flags_1.AutomationFlags,
|
|
174
|
+
};
|
|
175
|
+
exports.default = Whoami;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request deduplication manager
|
|
3
|
+
* Ensures only one in-flight request per unique key
|
|
4
|
+
*/
|
|
5
|
+
export interface DeduplicationStats {
|
|
6
|
+
hits: number;
|
|
7
|
+
misses: number;
|
|
8
|
+
pending: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class DeduplicationManager {
|
|
11
|
+
private pending;
|
|
12
|
+
private stats;
|
|
13
|
+
constructor();
|
|
14
|
+
/**
|
|
15
|
+
* Execute a function with deduplication
|
|
16
|
+
* If the same key is already in-flight, returns the existing promise
|
|
17
|
+
* @param key Unique identifier for the request
|
|
18
|
+
* @param fn Function to execute if no in-flight request exists
|
|
19
|
+
* @returns Promise resolving to the function result
|
|
20
|
+
*/
|
|
21
|
+
execute<T>(key: string, fn: () => Promise<T>): Promise<T>;
|
|
22
|
+
/**
|
|
23
|
+
* Get deduplication statistics
|
|
24
|
+
* @returns Object containing hits, misses, and pending count
|
|
25
|
+
*/
|
|
26
|
+
getStats(): DeduplicationStats;
|
|
27
|
+
/**
|
|
28
|
+
* Clear all pending requests and reset statistics
|
|
29
|
+
*/
|
|
30
|
+
clear(): void;
|
|
31
|
+
/**
|
|
32
|
+
* Safety cleanup for stale entries
|
|
33
|
+
* This should rarely be needed as promises clean themselves up
|
|
34
|
+
* @param _maxAge Maximum age in milliseconds (default: 30000)
|
|
35
|
+
*/
|
|
36
|
+
cleanup(_maxAge?: number): void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Global singleton instance for use across the application
|
|
40
|
+
*/
|
|
41
|
+
export declare const deduplicationManager: DeduplicationManager;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Request deduplication manager
|
|
4
|
+
* Ensures only one in-flight request per unique key
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.deduplicationManager = exports.DeduplicationManager = void 0;
|
|
8
|
+
class DeduplicationManager {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.pending = new Map();
|
|
11
|
+
this.stats = { hits: 0, misses: 0 };
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Execute a function with deduplication
|
|
15
|
+
* If the same key is already in-flight, returns the existing promise
|
|
16
|
+
* @param key Unique identifier for the request
|
|
17
|
+
* @param fn Function to execute if no in-flight request exists
|
|
18
|
+
* @returns Promise resolving to the function result
|
|
19
|
+
*/
|
|
20
|
+
async execute(key, fn) {
|
|
21
|
+
// Check for in-flight request
|
|
22
|
+
const existing = this.pending.get(key);
|
|
23
|
+
if (existing) {
|
|
24
|
+
this.stats.hits++;
|
|
25
|
+
return existing;
|
|
26
|
+
}
|
|
27
|
+
// Create new request
|
|
28
|
+
this.stats.misses++;
|
|
29
|
+
const promise = fn().finally(() => {
|
|
30
|
+
this.pending.delete(key);
|
|
31
|
+
});
|
|
32
|
+
this.pending.set(key, promise);
|
|
33
|
+
return promise;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get deduplication statistics
|
|
37
|
+
* @returns Object containing hits, misses, and pending count
|
|
38
|
+
*/
|
|
39
|
+
getStats() {
|
|
40
|
+
return {
|
|
41
|
+
...this.stats,
|
|
42
|
+
pending: this.pending.size,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Clear all pending requests and reset statistics
|
|
47
|
+
*/
|
|
48
|
+
clear() {
|
|
49
|
+
this.pending.clear();
|
|
50
|
+
this.stats = { hits: 0, misses: 0 };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Safety cleanup for stale entries
|
|
54
|
+
* This should rarely be needed as promises clean themselves up
|
|
55
|
+
* @param _maxAge Maximum age in milliseconds (default: 30000)
|
|
56
|
+
*/
|
|
57
|
+
cleanup(_maxAge = 30000) {
|
|
58
|
+
// Note: In practice, promises clean themselves up via finally()
|
|
59
|
+
// This is a safety mechanism for edge cases
|
|
60
|
+
const currentSize = this.pending.size;
|
|
61
|
+
if (currentSize > 0) {
|
|
62
|
+
// Log warning if cleanup is needed
|
|
63
|
+
console.warn(`DeduplicationManager cleanup called with ${currentSize} pending requests`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
exports.DeduplicationManager = DeduplicationManager;
|
|
68
|
+
/**
|
|
69
|
+
* Global singleton instance for use across the application
|
|
70
|
+
*/
|
|
71
|
+
exports.deduplicationManager = new DeduplicationManager();
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Envelope Standardization System for Notion CLI
|
|
3
|
+
*
|
|
4
|
+
* Provides consistent machine-readable output across all commands with:
|
|
5
|
+
* - Standard success/error envelopes
|
|
6
|
+
* - Metadata tracking (command, timestamp, execution time)
|
|
7
|
+
* - Exit code standardization (0=success, 1=API error, 2=CLI error)
|
|
8
|
+
* - Proper stdout/stderr separation
|
|
9
|
+
*/
|
|
10
|
+
import { NotionCLIErrorCode } from './errors/index';
|
|
11
|
+
/**
|
|
12
|
+
* Standard metadata included in all envelopes
|
|
13
|
+
*/
|
|
14
|
+
export interface EnvelopeMetadata {
|
|
15
|
+
/** ISO 8601 timestamp when the command was executed */
|
|
16
|
+
timestamp: string;
|
|
17
|
+
/** Full command name (e.g., "page retrieve", "db query") */
|
|
18
|
+
command: string;
|
|
19
|
+
/** Execution time in milliseconds */
|
|
20
|
+
execution_time_ms: number;
|
|
21
|
+
/** CLI version for debugging and compatibility */
|
|
22
|
+
version: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Success envelope structure
|
|
26
|
+
* Used when a command completes successfully
|
|
27
|
+
*/
|
|
28
|
+
export interface SuccessEnvelope<T = any> {
|
|
29
|
+
success: true;
|
|
30
|
+
data: T;
|
|
31
|
+
metadata: EnvelopeMetadata;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Error details structure
|
|
35
|
+
*/
|
|
36
|
+
export interface ErrorDetails {
|
|
37
|
+
/** Semantic error code (e.g., "DATABASE_NOT_FOUND", "RATE_LIMITED") */
|
|
38
|
+
code: NotionCLIErrorCode | string;
|
|
39
|
+
/** Human-readable error message */
|
|
40
|
+
message: string;
|
|
41
|
+
/** Additional context about the error */
|
|
42
|
+
details?: any;
|
|
43
|
+
/** Actionable suggestions for the user */
|
|
44
|
+
suggestions?: string[];
|
|
45
|
+
/** Original Notion API error (if applicable) */
|
|
46
|
+
notionError?: any;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Error envelope structure
|
|
50
|
+
* Used when a command fails
|
|
51
|
+
*/
|
|
52
|
+
export interface ErrorEnvelope {
|
|
53
|
+
success: false;
|
|
54
|
+
error: ErrorDetails;
|
|
55
|
+
metadata: Omit<EnvelopeMetadata, 'execution_time_ms'> & {
|
|
56
|
+
execution_time_ms?: number;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Union type for all envelope responses
|
|
61
|
+
*/
|
|
62
|
+
export type Envelope<T = any> = SuccessEnvelope<T> | ErrorEnvelope;
|
|
63
|
+
/**
|
|
64
|
+
* Exit codes for consistent process termination
|
|
65
|
+
*/
|
|
66
|
+
export declare enum ExitCode {
|
|
67
|
+
/** Command completed successfully */
|
|
68
|
+
SUCCESS = 0,
|
|
69
|
+
/** API/Notion error (auth, not found, rate limit, network, etc.) */
|
|
70
|
+
API_ERROR = 1,
|
|
71
|
+
/** CLI/validation error (invalid args, syntax, config issues) */
|
|
72
|
+
CLI_ERROR = 2
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Output flags that determine envelope formatting
|
|
76
|
+
*/
|
|
77
|
+
export interface OutputFlags {
|
|
78
|
+
json?: boolean;
|
|
79
|
+
'compact-json'?: boolean;
|
|
80
|
+
raw?: boolean;
|
|
81
|
+
markdown?: boolean;
|
|
82
|
+
pretty?: boolean;
|
|
83
|
+
csv?: boolean;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* EnvelopeFormatter - Core utility for creating and outputting envelopes
|
|
87
|
+
*/
|
|
88
|
+
export declare class EnvelopeFormatter {
|
|
89
|
+
private startTime;
|
|
90
|
+
private commandName;
|
|
91
|
+
private version;
|
|
92
|
+
/**
|
|
93
|
+
* Initialize formatter with command metadata
|
|
94
|
+
*
|
|
95
|
+
* @param commandName - Full command name (e.g., "page retrieve")
|
|
96
|
+
* @param version - CLI version from package.json
|
|
97
|
+
*/
|
|
98
|
+
constructor(commandName: string, version: string);
|
|
99
|
+
/**
|
|
100
|
+
* Create success envelope with data and metadata
|
|
101
|
+
*
|
|
102
|
+
* @param data - The actual response data
|
|
103
|
+
* @param additionalMetadata - Optional additional metadata fields
|
|
104
|
+
* @returns Success envelope ready for output
|
|
105
|
+
*/
|
|
106
|
+
wrapSuccess<T>(data: T, additionalMetadata?: Record<string, any>): SuccessEnvelope<T>;
|
|
107
|
+
/**
|
|
108
|
+
* Create error envelope from Error, NotionCLIError, or raw error object
|
|
109
|
+
*
|
|
110
|
+
* @param error - Error instance or error object
|
|
111
|
+
* @param additionalContext - Optional additional error context
|
|
112
|
+
* @returns Error envelope ready for output
|
|
113
|
+
*/
|
|
114
|
+
wrapError(error: any, additionalContext?: Record<string, any>): ErrorEnvelope;
|
|
115
|
+
/**
|
|
116
|
+
* Output envelope to stdout with proper formatting
|
|
117
|
+
* Handles flag-based format selection and stdout/stderr separation
|
|
118
|
+
*
|
|
119
|
+
* @param envelope - Success or error envelope
|
|
120
|
+
* @param flags - Output format flags
|
|
121
|
+
* @param logFn - Logging function (typically this.log from Command)
|
|
122
|
+
*/
|
|
123
|
+
outputEnvelope(envelope: Envelope, flags: OutputFlags, logFn?: (message: string) => void): void;
|
|
124
|
+
/**
|
|
125
|
+
* Get appropriate exit code for the envelope
|
|
126
|
+
*
|
|
127
|
+
* @param envelope - Success or error envelope
|
|
128
|
+
* @returns Exit code (0, 1, or 2)
|
|
129
|
+
*/
|
|
130
|
+
getExitCode(envelope: Envelope): ExitCode;
|
|
131
|
+
/**
|
|
132
|
+
* Write diagnostic messages to stderr (won't pollute JSON on stdout)
|
|
133
|
+
* Useful for retry messages, cache hits, debug info, etc.
|
|
134
|
+
*
|
|
135
|
+
* @param message - Diagnostic message
|
|
136
|
+
* @param level - Message level (info, warn, error)
|
|
137
|
+
*/
|
|
138
|
+
static writeDiagnostic(message: string, level?: 'info' | 'warn' | 'error'): void;
|
|
139
|
+
/**
|
|
140
|
+
* Helper to log retry attempts to stderr (doesn't pollute JSON output)
|
|
141
|
+
*
|
|
142
|
+
* @param attempt - Retry attempt number
|
|
143
|
+
* @param maxRetries - Maximum retry attempts
|
|
144
|
+
* @param delay - Delay before next retry in milliseconds
|
|
145
|
+
*/
|
|
146
|
+
static logRetry(attempt: number, maxRetries: number, delay: number): void;
|
|
147
|
+
/**
|
|
148
|
+
* Helper to log cache hits to stderr (for debugging)
|
|
149
|
+
*
|
|
150
|
+
* @param cacheKey - Cache key that was hit
|
|
151
|
+
*/
|
|
152
|
+
static logCacheHit(cacheKey: string): void;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Convenience function to create an envelope formatter
|
|
156
|
+
*
|
|
157
|
+
* @param commandName - Full command name
|
|
158
|
+
* @param version - CLI version
|
|
159
|
+
* @returns New EnvelopeFormatter instance
|
|
160
|
+
*/
|
|
161
|
+
export declare function createEnvelopeFormatter(commandName: string, version: string): EnvelopeFormatter;
|
|
162
|
+
/**
|
|
163
|
+
* Type guard to check if envelope is a success envelope
|
|
164
|
+
*/
|
|
165
|
+
export declare function isSuccessEnvelope<T>(envelope: Envelope<T>): envelope is SuccessEnvelope<T>;
|
|
166
|
+
/**
|
|
167
|
+
* Type guard to check if envelope is an error envelope
|
|
168
|
+
*/
|
|
169
|
+
export declare function isErrorEnvelope(envelope: Envelope): envelope is ErrorEnvelope;
|