gudong-inbox-cli 1.0.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 +80 -0
- package/dist/cloud/factory.d.ts +4 -0
- package/dist/cloud/factory.js +27 -0
- package/dist/cloud/factory.js.map +1 -0
- package/dist/cloud/s3.d.ts +18 -0
- package/dist/cloud/s3.js +63 -0
- package/dist/cloud/s3.js.map +1 -0
- package/dist/cloud/types.d.ts +12 -0
- package/dist/cloud/types.js +2 -0
- package/dist/cloud/types.js.map +1 -0
- package/dist/cloud/webdav.d.ts +14 -0
- package/dist/cloud/webdav.js +39 -0
- package/dist/cloud/webdav.js.map +1 -0
- package/dist/config.d.ts +36 -0
- package/dist/config.js +106 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +352 -0
- package/dist/index.js.map +1 -0
- package/dist/note/parser.d.ts +2 -0
- package/dist/note/parser.js +110 -0
- package/dist/note/parser.js.map +1 -0
- package/dist/note/parser.test.d.ts +1 -0
- package/dist/note/parser.test.js +137 -0
- package/dist/note/parser.test.js.map +1 -0
- package/dist/note/service.d.ts +76 -0
- package/dist/note/service.js +360 -0
- package/dist/note/service.js.map +1 -0
- package/dist/note/service.test.d.ts +1 -0
- package/dist/note/service.test.js +274 -0
- package/dist/note/service.test.js.map +1 -0
- package/dist/note/types.d.ts +72 -0
- package/dist/note/types.js +16 -0
- package/dist/note/types.js.map +1 -0
- package/package.json +47 -0
- package/skill/SKILL.md +103 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 gudong
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# gudong-inbox-cli
|
|
2
|
+
|
|
3
|
+
> CLI tool that lets AI agents read and write your inBox notes from S3/WebDAV. No MCP, no server, just a command.
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
[inBox](https://inbox.gudong.site) is a local-first notes app that syncs via S3 or WebDAV. This CLI gives AI coding agents (Codex, Claude, Cursor etc.) direct access to your notes through simple shell commands — no MCP protocol, no config files to fight with, no app restarts.
|
|
8
|
+
|
|
9
|
+
Inspired by [Agently Mail](https://agent.qq.com) — the idea that a CLI + Skill is simpler and more reliable than MCP for AI agent integration.
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Install
|
|
15
|
+
npm install -g gudong-inbox-cli
|
|
16
|
+
|
|
17
|
+
# Configure (S3 example)
|
|
18
|
+
gudong-inbox config init \
|
|
19
|
+
--cloud-type s3 \
|
|
20
|
+
--endpoint https://s3.cn-south-1.qiniucs.com \
|
|
21
|
+
--bucket my-bucket \
|
|
22
|
+
--access-key AKID... \
|
|
23
|
+
--secret-key SECRET... \
|
|
24
|
+
--region cn-south-1 \
|
|
25
|
+
--work-dir inBox
|
|
26
|
+
|
|
27
|
+
# Verify
|
|
28
|
+
gudong-inbox config test
|
|
29
|
+
|
|
30
|
+
# Use
|
|
31
|
+
gudong-inbox list --limit 5
|
|
32
|
+
gudong-inbox search --query "Flutter"
|
|
33
|
+
gudong-inbox read --id b10a4576df11401c8673
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Commands
|
|
37
|
+
|
|
38
|
+
| Command | Description |
|
|
39
|
+
|---------|-------------|
|
|
40
|
+
| `list` | List note summaries (with time range and date field filtering) |
|
|
41
|
+
| `read` | Read full content of a single note by ID |
|
|
42
|
+
| `search` | Search notes by keyword in title/body/tags |
|
|
43
|
+
| `tags` | List all tags with usage counts |
|
|
44
|
+
| `create` | Create a new note |
|
|
45
|
+
| `update` | Partially update an existing note |
|
|
46
|
+
| `delete` | Soft-delete a note (requires --confirm) |
|
|
47
|
+
| `config init` | Set up cloud storage credentials |
|
|
48
|
+
| `config show` | Show current config (secrets masked) |
|
|
49
|
+
| `config test` | Test connection to cloud storage |
|
|
50
|
+
|
|
51
|
+
## Config
|
|
52
|
+
|
|
53
|
+
Config is stored at `~/.gudong-inbox/config.json` (created by `config init`). Environment variables (`CLOUD_TYPE`, `S3_ENDPOINT`, etc.) work as fallback for compatibility with the MCP version.
|
|
54
|
+
|
|
55
|
+
## Skill for AI Agents
|
|
56
|
+
|
|
57
|
+
A `skill/SKILL.md` is included. Install it to your AI agent's skill directory:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# For Codex
|
|
61
|
+
cp skill/SKILL.md ~/.codex/skills/gudong-inbox/SKILL.md
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Once installed, the AI agent will automatically discover the skill and know when to use `gudong-inbox` commands — no MCP, no restart needed.
|
|
65
|
+
|
|
66
|
+
## How it works
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
AI agent (Codex / Claude / Cursor)
|
|
70
|
+
↓ exec_command
|
|
71
|
+
gudong-inbox (standalone CLI process)
|
|
72
|
+
↓ S3 ListObjects / WebDAV PROPFIND
|
|
73
|
+
your cloud storage
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Each command is a standalone process that connects directly to your S3/WebDAV storage, reads inBox note JSON files, and outputs structured JSON. No persistent server, no protocol handshake, no startup failures to debug.
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
MIT
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { S3Provider } from './s3.js';
|
|
2
|
+
import { WebdavProvider } from './webdav.js';
|
|
3
|
+
export function createCloudProvider(cfg) {
|
|
4
|
+
if (cfg.cloudType === 's3' && cfg.s3) {
|
|
5
|
+
return new S3Provider({
|
|
6
|
+
endpoint: cfg.s3.endpoint,
|
|
7
|
+
bucket: cfg.s3.bucket,
|
|
8
|
+
accessKey: cfg.s3.accessKey,
|
|
9
|
+
secretKey: cfg.s3.secretKey,
|
|
10
|
+
region: cfg.s3.region,
|
|
11
|
+
forcePathStyle: cfg.s3.forcePathStyle,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
if (cfg.cloudType === 'webdav' && cfg.webdav) {
|
|
15
|
+
return new WebdavProvider({
|
|
16
|
+
url: cfg.webdav.url,
|
|
17
|
+
username: cfg.webdav.username,
|
|
18
|
+
password: cfg.webdav.password,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`unsupported cloud type or missing config: ${cfg.cloudType}`);
|
|
22
|
+
}
|
|
23
|
+
export function notesDirFor(workDir, type) {
|
|
24
|
+
const trimmed = workDir.replace(/^\/+|\/+$/g, '');
|
|
25
|
+
return type === 'webdav' ? `/${trimmed}/notes` : `${trimmed}/notes`;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/cloud/factory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7C,MAAM,UAAU,mBAAmB,CAAC,GAAiB;IACnD,IAAI,GAAG,CAAC,SAAS,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;QACrC,OAAO,IAAI,UAAU,CAAC;YACpB,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,QAAQ;YACzB,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,MAAM;YACrB,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC,SAAS;YAC3B,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC,SAAS;YAC3B,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,MAAM;YACrB,cAAc,EAAE,GAAG,CAAC,EAAE,CAAC,cAAc;SACtC,CAAC,CAAC;IACL,CAAC;IACD,IAAI,GAAG,CAAC,SAAS,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;QAC7C,OAAO,IAAI,cAAc,CAAC;YACxB,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG;YACnB,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,QAAQ;YAC7B,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,QAAQ;SAC9B,CAAC,CAAC;IACL,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,6CAA6C,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,IAAqB;IAChE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IAClD,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,OAAO,QAAQ,CAAC,CAAC,CAAC,GAAG,OAAO,QAAQ,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { CloudProvider, NoteFile } from './types.js';
|
|
2
|
+
export interface S3ProviderConfig {
|
|
3
|
+
endpoint: string;
|
|
4
|
+
bucket: string;
|
|
5
|
+
accessKey: string;
|
|
6
|
+
secretKey: string;
|
|
7
|
+
region: string;
|
|
8
|
+
forcePathStyle?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare class S3Provider implements CloudProvider {
|
|
11
|
+
readonly type: "s3";
|
|
12
|
+
private readonly client;
|
|
13
|
+
private readonly bucket;
|
|
14
|
+
constructor(cfg: S3ProviderConfig);
|
|
15
|
+
listNotes(notesDir: string): Promise<NoteFile[]>;
|
|
16
|
+
readNote(key: string): Promise<Buffer>;
|
|
17
|
+
writeNote(key: string, data: Buffer): Promise<void>;
|
|
18
|
+
}
|
package/dist/cloud/s3.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { S3Client, ListObjectsV2Command, GetObjectCommand, PutObjectCommand, } from '@aws-sdk/client-s3';
|
|
2
|
+
const NOTE_ID_PATTERN = /note-([a-f0-9]{20})\.json$/;
|
|
3
|
+
export class S3Provider {
|
|
4
|
+
type = 's3';
|
|
5
|
+
client;
|
|
6
|
+
bucket;
|
|
7
|
+
constructor(cfg) {
|
|
8
|
+
this.bucket = cfg.bucket;
|
|
9
|
+
this.client = new S3Client({
|
|
10
|
+
endpoint: cfg.endpoint,
|
|
11
|
+
region: cfg.region,
|
|
12
|
+
credentials: {
|
|
13
|
+
accessKeyId: cfg.accessKey,
|
|
14
|
+
secretAccessKey: cfg.secretKey,
|
|
15
|
+
},
|
|
16
|
+
forcePathStyle: cfg.forcePathStyle ?? false,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
async listNotes(notesDir) {
|
|
20
|
+
const prefix = notesDir.endsWith('/') ? notesDir : `${notesDir}/`;
|
|
21
|
+
const out = [];
|
|
22
|
+
let token;
|
|
23
|
+
do {
|
|
24
|
+
const res = await this.client.send(new ListObjectsV2Command({
|
|
25
|
+
Bucket: this.bucket,
|
|
26
|
+
Prefix: prefix,
|
|
27
|
+
ContinuationToken: token,
|
|
28
|
+
}));
|
|
29
|
+
for (const obj of res.Contents ?? []) {
|
|
30
|
+
if (!obj.Key)
|
|
31
|
+
continue;
|
|
32
|
+
const m = obj.Key.match(NOTE_ID_PATTERN);
|
|
33
|
+
if (!m)
|
|
34
|
+
continue;
|
|
35
|
+
out.push({
|
|
36
|
+
id: m[1],
|
|
37
|
+
key: obj.Key,
|
|
38
|
+
lastModified: obj.LastModified,
|
|
39
|
+
size: obj.Size,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
token = res.IsTruncated ? res.NextContinuationToken : undefined;
|
|
43
|
+
} while (token);
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
async readNote(key) {
|
|
47
|
+
const res = await this.client.send(new GetObjectCommand({ Bucket: this.bucket, Key: key }));
|
|
48
|
+
if (!res.Body) {
|
|
49
|
+
throw new Error(`empty body for s3://${this.bucket}/${key}`);
|
|
50
|
+
}
|
|
51
|
+
const bytes = await res.Body.transformToByteArray();
|
|
52
|
+
return Buffer.from(bytes);
|
|
53
|
+
}
|
|
54
|
+
async writeNote(key, data) {
|
|
55
|
+
await this.client.send(new PutObjectCommand({
|
|
56
|
+
Bucket: this.bucket,
|
|
57
|
+
Key: key,
|
|
58
|
+
Body: data,
|
|
59
|
+
ContentType: 'application/json',
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=s3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3.js","sourceRoot":"","sources":["../../src/cloud/s3.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface NoteFile {
|
|
2
|
+
id: string;
|
|
3
|
+
key: string;
|
|
4
|
+
lastModified?: Date;
|
|
5
|
+
size?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface CloudProvider {
|
|
8
|
+
readonly type: 's3' | 'webdav';
|
|
9
|
+
listNotes(notesDir: string): Promise<NoteFile[]>;
|
|
10
|
+
readNote(key: string): Promise<Buffer>;
|
|
11
|
+
writeNote(key: string, data: Buffer): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/cloud/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CloudProvider, NoteFile } from './types.js';
|
|
2
|
+
export interface WebdavProviderConfig {
|
|
3
|
+
url: string;
|
|
4
|
+
username: string;
|
|
5
|
+
password: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class WebdavProvider implements CloudProvider {
|
|
8
|
+
readonly type: "webdav";
|
|
9
|
+
private readonly client;
|
|
10
|
+
constructor(cfg: WebdavProviderConfig);
|
|
11
|
+
listNotes(notesDir: string): Promise<NoteFile[]>;
|
|
12
|
+
readNote(key: string): Promise<Buffer>;
|
|
13
|
+
writeNote(key: string, data: Buffer): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createClient } from 'webdav';
|
|
2
|
+
const NOTE_ID_PATTERN = /note-([a-f0-9]{20})\.json$/;
|
|
3
|
+
export class WebdavProvider {
|
|
4
|
+
type = 'webdav';
|
|
5
|
+
client;
|
|
6
|
+
constructor(cfg) {
|
|
7
|
+
this.client = createClient(cfg.url, {
|
|
8
|
+
username: cfg.username,
|
|
9
|
+
password: cfg.password,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
async listNotes(notesDir) {
|
|
13
|
+
const dir = notesDir.startsWith('/') ? notesDir : `/${notesDir}`;
|
|
14
|
+
const contents = (await this.client.getDirectoryContents(dir, {
|
|
15
|
+
glob: 'note-*.json',
|
|
16
|
+
}));
|
|
17
|
+
const out = [];
|
|
18
|
+
for (const item of contents) {
|
|
19
|
+
const m = item.basename.match(NOTE_ID_PATTERN);
|
|
20
|
+
if (!m)
|
|
21
|
+
continue;
|
|
22
|
+
out.push({
|
|
23
|
+
id: m[1],
|
|
24
|
+
key: item.filename,
|
|
25
|
+
lastModified: item.lastmod ? new Date(item.lastmod) : undefined,
|
|
26
|
+
size: item.size,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
async readNote(key) {
|
|
32
|
+
const buf = await this.client.getFileContents(key, { format: 'binary' });
|
|
33
|
+
return Buffer.isBuffer(buf) ? buf : Buffer.from(buf);
|
|
34
|
+
}
|
|
35
|
+
async writeNote(key, data) {
|
|
36
|
+
await this.client.putFileContents(key, data, { overwrite: true });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=webdav.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webdav.js","sourceRoot":"","sources":["../../src/cloud/webdav.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAStC,MAAM,eAAe,GAAG,4BAA4B,CAAC;AASrD,MAAM,OAAO,cAAc;IAChB,IAAI,GAAG,QAAiB,CAAC;IACjB,MAAM,CAAkC;IAEzD,YAAY,GAAyB;QACnC,IAAI,CAAC,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE;YAClC,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,QAAQ,EAAE,GAAG,CAAC,QAAQ;SACvB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,QAAgB;QAC9B,MAAM,GAAG,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,QAAQ,EAAE,CAAC;QACjE,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,GAAG,EAAE;YAC5D,IAAI,EAAE,aAAa;SACpB,CAAC,CAAqB,CAAC;QACxB,MAAM,GAAG,GAAe,EAAE,CAAC;QAC3B,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;YAC/C,IAAI,CAAC,CAAC;gBAAE,SAAS;YACjB,GAAG,CAAC,IAAI,CAAC;gBACP,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;gBACR,GAAG,EAAE,IAAI,CAAC,QAAQ;gBAClB,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;gBAC/D,IAAI,EAAE,IAAI,CAAC,IAAI;aAChB,CAAC,CAAC;QACL,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,GAAW;QACxB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QACzE,OAAO,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAkB,CAAC,CAAC;IACtE,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,GAAW,EAAE,IAAY;QACvC,MAAM,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpE,CAAC;CACF"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface S3Config {
|
|
2
|
+
endpoint: string;
|
|
3
|
+
bucket: string;
|
|
4
|
+
accessKey: string;
|
|
5
|
+
secretKey: string;
|
|
6
|
+
region: string;
|
|
7
|
+
forcePathStyle: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface WebdavConfig {
|
|
10
|
+
url: string;
|
|
11
|
+
username: string;
|
|
12
|
+
password: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ParsedConfig {
|
|
15
|
+
cloudType: 's3' | 'webdav';
|
|
16
|
+
workDir: string;
|
|
17
|
+
cacheTtlMs: number;
|
|
18
|
+
s3?: S3Config;
|
|
19
|
+
webdav?: WebdavConfig;
|
|
20
|
+
}
|
|
21
|
+
interface RawConfig {
|
|
22
|
+
cloud_type?: string;
|
|
23
|
+
work_dir?: string;
|
|
24
|
+
cache_ttl_ms?: number;
|
|
25
|
+
s3?: Partial<S3Config>;
|
|
26
|
+
webdav?: Partial<WebdavConfig>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Load config: file first (~/.gudong-inbox/config.json), then env vars as fallback.
|
|
30
|
+
* Env vars use the same names as the MCP version for compatibility.
|
|
31
|
+
*/
|
|
32
|
+
export declare function loadConfig(): ParsedConfig;
|
|
33
|
+
export declare function saveConfig(cfg: RawConfig): void;
|
|
34
|
+
export declare function configExists(): boolean;
|
|
35
|
+
export declare function getConfigPath(): string;
|
|
36
|
+
export {};
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, chmodSync } from 'node:fs';
|
|
2
|
+
import { renameSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
const CONFIG_DIR = join(homedir(), '.gudong-inbox');
|
|
6
|
+
const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
|
|
7
|
+
// Legacy path from the old name — we migrate it once if the new config is missing.
|
|
8
|
+
const LEGACY_CONFIG_FILE = join(homedir(), '.gudong-inbox', 'config.json');
|
|
9
|
+
/**
|
|
10
|
+
* Load config: file first (~/.gudong-inbox/config.json), then env vars as fallback.
|
|
11
|
+
* Env vars use the same names as the MCP version for compatibility.
|
|
12
|
+
*/
|
|
13
|
+
export function loadConfig() {
|
|
14
|
+
let raw = {};
|
|
15
|
+
// 1. Try config file, migrating from the legacy path on first run
|
|
16
|
+
if (!existsSync(CONFIG_FILE) && existsSync(LEGACY_CONFIG_FILE)) {
|
|
17
|
+
if (!existsSync(CONFIG_DIR))
|
|
18
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
19
|
+
renameSync(LEGACY_CONFIG_FILE, CONFIG_FILE);
|
|
20
|
+
chmodSync(CONFIG_FILE, 0o600);
|
|
21
|
+
process.stderr.write(`[gudong-inbox] migrated config from legacy path ~/.gudong-inbox/ to ~/.gudong-inbox/\n`);
|
|
22
|
+
}
|
|
23
|
+
if (existsSync(CONFIG_FILE)) {
|
|
24
|
+
const text = readFileSync(CONFIG_FILE, 'utf-8');
|
|
25
|
+
raw = JSON.parse(text);
|
|
26
|
+
}
|
|
27
|
+
// 2. Env vars fill gaps where config file values are missing
|
|
28
|
+
const env = process.env;
|
|
29
|
+
const cloudType = raw.cloud_type ?? env.CLOUD_TYPE;
|
|
30
|
+
if (!cloudType || (cloudType !== 's3' && cloudType !== 'webdav')) {
|
|
31
|
+
fail('CLOUD_TYPE must be "s3" or "webdav". Run `gudong-inbox config init` first.');
|
|
32
|
+
}
|
|
33
|
+
const workDir = raw.work_dir ?? env.WORK_DIR ?? 'inBox';
|
|
34
|
+
const cacheTtlMs = raw.cache_ttl_ms ?? Number(env.CACHE_TTL_MS ?? 60000);
|
|
35
|
+
if (cloudType === 's3') {
|
|
36
|
+
const s3 = raw.s3 ?? {};
|
|
37
|
+
const endpoint = s3.endpoint ?? env.S3_ENDPOINT;
|
|
38
|
+
const bucket = s3.bucket ?? env.S3_BUCKET;
|
|
39
|
+
const accessKey = s3.accessKey ?? env.S3_ACCESS_KEY;
|
|
40
|
+
const secretKey = s3.secretKey ?? env.S3_SECRET_KEY;
|
|
41
|
+
const region = s3.region ?? env.S3_REGION ?? 'auto';
|
|
42
|
+
const forcePathStyle = s3.forcePathStyle != null ? s3.forcePathStyle : parseBool(env.S3_FORCE_PATH_STYLE, false);
|
|
43
|
+
if (!endpoint)
|
|
44
|
+
fail('S3_ENDPOINT is required. Run `gudong-inbox config init` first.');
|
|
45
|
+
if (!bucket)
|
|
46
|
+
fail('S3_BUCKET is required. Run `gudong-inbox config init` first.');
|
|
47
|
+
if (!accessKey)
|
|
48
|
+
fail('S3_ACCESS_KEY is required. Run `gudong-inbox config init` first.');
|
|
49
|
+
if (!secretKey)
|
|
50
|
+
fail('S3_SECRET_KEY is required. Run `gudong-inbox config init` first.');
|
|
51
|
+
return {
|
|
52
|
+
cloudType: 's3',
|
|
53
|
+
workDir,
|
|
54
|
+
cacheTtlMs,
|
|
55
|
+
s3: { endpoint: ensureHttps(endpoint), bucket, accessKey, secretKey, region, forcePathStyle },
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
// webdav
|
|
59
|
+
const webdav = raw.webdav ?? {};
|
|
60
|
+
const url = webdav.url ?? env.WEBDAV_URL;
|
|
61
|
+
const username = webdav.username ?? env.WEBDAV_USERNAME;
|
|
62
|
+
const password = webdav.password ?? env.WEBDAV_PASSWORD;
|
|
63
|
+
if (!url)
|
|
64
|
+
fail('WEBDAV_URL is required. Run `gudong-inbox config init` first.');
|
|
65
|
+
if (!username)
|
|
66
|
+
fail('WEBDAV_USERNAME is required. Run `gudong-inbox config init` first.');
|
|
67
|
+
if (!password)
|
|
68
|
+
fail('WEBDAV_PASSWORD is required. Run `gudong-inbox config init` first.');
|
|
69
|
+
return {
|
|
70
|
+
cloudType: 'webdav',
|
|
71
|
+
workDir,
|
|
72
|
+
cacheTtlMs,
|
|
73
|
+
webdav: { url, username, password },
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export function saveConfig(cfg) {
|
|
77
|
+
if (!existsSync(CONFIG_DIR))
|
|
78
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
79
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2) + '\n', 'utf-8');
|
|
80
|
+
chmodSync(CONFIG_FILE, 0o600);
|
|
81
|
+
}
|
|
82
|
+
export function configExists() {
|
|
83
|
+
return existsSync(CONFIG_FILE);
|
|
84
|
+
}
|
|
85
|
+
export function getConfigPath() {
|
|
86
|
+
return CONFIG_FILE;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Auto-prepend https:// if the endpoint is missing a protocol.
|
|
90
|
+
* This prevents the "Invalid URL" error that plagued the MCP version.
|
|
91
|
+
*/
|
|
92
|
+
function ensureHttps(endpoint) {
|
|
93
|
+
if (endpoint.startsWith('http://') || endpoint.startsWith('https://'))
|
|
94
|
+
return endpoint;
|
|
95
|
+
return `https://${endpoint}`;
|
|
96
|
+
}
|
|
97
|
+
function parseBool(v, fallback) {
|
|
98
|
+
if (v == null)
|
|
99
|
+
return fallback;
|
|
100
|
+
return v === 'true' || v === '1';
|
|
101
|
+
}
|
|
102
|
+
function fail(msg) {
|
|
103
|
+
process.stderr.write(`[gudong-inbox] config error: ${msg}\n`);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED