@iflow-mcp/joungminsung-opendocuments 0.2.1
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 +144 -0
- package/dist/commands/ask.d.ts +3 -0
- package/dist/commands/ask.d.ts.map +1 -0
- package/dist/commands/ask.js +99 -0
- package/dist/commands/ask.js.map +1 -0
- package/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +87 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/completion.d.ts +3 -0
- package/dist/commands/completion.d.ts.map +1 -0
- package/dist/commands/completion.js +88 -0
- package/dist/commands/completion.js.map +1 -0
- package/dist/commands/config-cmd.d.ts +3 -0
- package/dist/commands/config-cmd.d.ts.map +1 -0
- package/dist/commands/config-cmd.js +61 -0
- package/dist/commands/config-cmd.js.map +1 -0
- package/dist/commands/connector.d.ts +3 -0
- package/dist/commands/connector.d.ts.map +1 -0
- package/dist/commands/connector.js +84 -0
- package/dist/commands/connector.js.map +1 -0
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +151 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/document.d.ts +3 -0
- package/dist/commands/document.d.ts.map +1 -0
- package/dist/commands/document.js +78 -0
- package/dist/commands/document.js.map +1 -0
- package/dist/commands/export-cmd.d.ts +3 -0
- package/dist/commands/export-cmd.d.ts.map +1 -0
- package/dist/commands/export-cmd.js +44 -0
- package/dist/commands/export-cmd.js.map +1 -0
- package/dist/commands/import-cmd.d.ts +3 -0
- package/dist/commands/import-cmd.d.ts.map +1 -0
- package/dist/commands/import-cmd.js +39 -0
- package/dist/commands/import-cmd.js.map +1 -0
- package/dist/commands/index-cmd.d.ts +3 -0
- package/dist/commands/index-cmd.d.ts.map +1 -0
- package/dist/commands/index-cmd.js +76 -0
- package/dist/commands/index-cmd.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +408 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/plugin.d.ts +3 -0
- package/dist/commands/plugin.d.ts.map +1 -0
- package/dist/commands/plugin.js +296 -0
- package/dist/commands/plugin.js.map +1 -0
- package/dist/commands/search.d.ts +3 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +40 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/start.d.ts +3 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +120 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/stop.d.ts +3 -0
- package/dist/commands/stop.d.ts.map +1 -0
- package/dist/commands/stop.js +55 -0
- package/dist/commands/stop.js.map +1 -0
- package/dist/commands/upgrade.d.ts +3 -0
- package/dist/commands/upgrade.d.ts.map +1 -0
- package/dist/commands/upgrade.js +16 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/commands/workspace.d.ts +3 -0
- package/dist/commands/workspace.d.ts.map +1 -0
- package/dist/commands/workspace.js +85 -0
- package/dist/commands/workspace.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/bootstrap.d.ts +4 -0
- package/dist/utils/bootstrap.d.ts.map +1 -0
- package/dist/utils/bootstrap.js +17 -0
- package/dist/utils/bootstrap.js.map +1 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# OpenDocuments
|
|
2
|
+
|
|
3
|
+
**Open source RAG tool for AI document search** — connect GitHub, Notion, Google Drive and ask questions with cited answers.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/joungminsung/OpenDocuments/actions)
|
|
6
|
+
[](https://www.npmjs.com/package/opendocuments)
|
|
7
|
+
[](https://github.com/joungminsung/OpenDocuments/blob/main/LICENSE)
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g opendocuments
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
opendocuments init # Auto-detects Ollama, pulls models, validates API keys
|
|
19
|
+
opendocuments start # Web UI at http://localhost:3000
|
|
20
|
+
opendocuments index ./docs
|
|
21
|
+
opendocuments ask "How does authentication work?"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## What It Does
|
|
25
|
+
|
|
26
|
+
OpenDocuments connects your scattered documents and answers questions with AI:
|
|
27
|
+
|
|
28
|
+
- **10+ data sources** — GitHub, Notion, Google Drive, Confluence, S3, Swagger, web pages
|
|
29
|
+
- **12+ file formats** — PDF, DOCX, Excel, HTML, Jupyter, code, email, PowerPoint
|
|
30
|
+
- **Local or cloud AI** — Ollama (data stays local) or OpenAI, Claude, Gemini, Grok
|
|
31
|
+
- **Korean + English** — cross-lingual search finds docs regardless of language
|
|
32
|
+
- **MCP server** — works with Claude Code, Cursor, and any MCP-compatible AI tool
|
|
33
|
+
|
|
34
|
+
## CLI Commands
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Ask questions
|
|
38
|
+
opendocuments ask "query" # Single question
|
|
39
|
+
opendocuments ask # Interactive REPL
|
|
40
|
+
opendocuments search "keyword" --top 5 # Vector search (no LLM)
|
|
41
|
+
|
|
42
|
+
# Documents
|
|
43
|
+
opendocuments index ./docs --watch # Index + auto-reindex
|
|
44
|
+
opendocuments document list # List indexed docs
|
|
45
|
+
opendocuments document delete <id> # Soft-delete
|
|
46
|
+
|
|
47
|
+
# Connectors
|
|
48
|
+
opendocuments connector sync # Sync all sources
|
|
49
|
+
opendocuments connector status # Check sync status
|
|
50
|
+
|
|
51
|
+
# Server
|
|
52
|
+
opendocuments start # HTTP + Web UI
|
|
53
|
+
opendocuments start --mcp-only # MCP server (stdio)
|
|
54
|
+
opendocuments stop # Stop server
|
|
55
|
+
opendocuments doctor # Health diagnostics
|
|
56
|
+
|
|
57
|
+
# Plugins
|
|
58
|
+
opendocuments plugin create my-parser --type parser
|
|
59
|
+
opendocuments plugin list
|
|
60
|
+
opendocuments plugin publish
|
|
61
|
+
|
|
62
|
+
# Admin
|
|
63
|
+
opendocuments auth create-key --name "bot" --role member
|
|
64
|
+
opendocuments export --output ./backup
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Configuration
|
|
68
|
+
|
|
69
|
+
`opendocuments init` generates `opendocuments.config.ts`:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { defineConfig } from 'opendocuments-core'
|
|
73
|
+
|
|
74
|
+
export default defineConfig({
|
|
75
|
+
workspace: 'my-team',
|
|
76
|
+
mode: 'personal',
|
|
77
|
+
|
|
78
|
+
model: {
|
|
79
|
+
provider: 'ollama', // or 'openai', 'anthropic', 'google', 'grok'
|
|
80
|
+
llm: 'qwen2.5:14b',
|
|
81
|
+
embedding: 'bge-m3',
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
rag: { profile: 'balanced' }, // 'fast' | 'balanced' | 'precise'
|
|
85
|
+
|
|
86
|
+
connectors: [
|
|
87
|
+
{ type: 'github', repo: 'org/repo', token: process.env.GITHUB_TOKEN },
|
|
88
|
+
{ type: 'notion', token: process.env.NOTION_TOKEN },
|
|
89
|
+
],
|
|
90
|
+
|
|
91
|
+
plugins: [
|
|
92
|
+
'@opendocuments/parser-pdf',
|
|
93
|
+
'@opendocuments/parser-docx',
|
|
94
|
+
],
|
|
95
|
+
})
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
API keys are stored in `.env` and loaded automatically.
|
|
99
|
+
|
|
100
|
+
## MCP Server
|
|
101
|
+
|
|
102
|
+
Use OpenDocuments as a knowledge base for AI coding tools:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"mcpServers": {
|
|
107
|
+
"opendocuments": {
|
|
108
|
+
"command": "opendocuments",
|
|
109
|
+
"args": ["start", "--mcp-only"]
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
19 tools available: search, ask, index, document management, connector sync, stats.
|
|
116
|
+
|
|
117
|
+
## Docker
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
docker compose up -d # Cloud LLM
|
|
121
|
+
docker compose --profile with-ollama up -d # Local LLM
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## RAG Profiles
|
|
125
|
+
|
|
126
|
+
| | fast | balanced | precise |
|
|
127
|
+
|--|------|----------|---------|
|
|
128
|
+
| Speed | ~1s | ~3s | ~5s+ |
|
|
129
|
+
| Search depth | 10 docs | 20 docs | 50 docs |
|
|
130
|
+
| Reranking | Off | On | On |
|
|
131
|
+
| Cross-lingual | Off | KR + EN | KR + EN |
|
|
132
|
+
| Hallucination guard | Off | Checks | Strict |
|
|
133
|
+
|
|
134
|
+
## Links
|
|
135
|
+
|
|
136
|
+
- **GitHub**: https://github.com/joungminsung/OpenDocuments
|
|
137
|
+
- **Documentation**: https://joungminsung.github.io/OpenDocuments
|
|
138
|
+
- **Changelog**: https://github.com/joungminsung/OpenDocuments/releases
|
|
139
|
+
- **Issues**: https://github.com/joungminsung/OpenDocuments/issues
|
|
140
|
+
- **Security**: https://github.com/joungminsung/OpenDocuments/blob/main/SECURITY.md
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../../src/commands/ask.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAKnC,wBAAgB,UAAU,YA6FzB"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { log } from 'opendocuments-core';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { getContext, shutdownContext } from '../utils/bootstrap.js';
|
|
5
|
+
export function askCommand() {
|
|
6
|
+
return new Command('ask')
|
|
7
|
+
.description('Ask a question about indexed documents')
|
|
8
|
+
.argument('[query]', 'The question to ask')
|
|
9
|
+
.option('--profile <profile>', 'RAG profile: fast, balanced, precise', 'balanced')
|
|
10
|
+
.option('--json', 'Output as JSON')
|
|
11
|
+
.option('--stdin', 'Read from stdin')
|
|
12
|
+
.action(async (query, opts) => {
|
|
13
|
+
// Check for piped stdin
|
|
14
|
+
if (opts.stdin || !process.stdin.isTTY) {
|
|
15
|
+
try {
|
|
16
|
+
const chunks = [];
|
|
17
|
+
for await (const chunk of process.stdin)
|
|
18
|
+
chunks.push(chunk);
|
|
19
|
+
const stdinContent = Buffer.concat(chunks).toString('utf-8').trim();
|
|
20
|
+
if (stdinContent) {
|
|
21
|
+
query = query ? `${query}\n\nContext:\n${stdinContent}` : stdinContent;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch { }
|
|
25
|
+
}
|
|
26
|
+
if (!query) {
|
|
27
|
+
// Interactive REPL mode
|
|
28
|
+
const ctx = await getContext();
|
|
29
|
+
const readline = await import('node:readline');
|
|
30
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
31
|
+
log.heading('OpenDocuments Interactive');
|
|
32
|
+
log.dim(`Profile: ${opts.profile} | Type /quit to exit`);
|
|
33
|
+
const askLine = () => {
|
|
34
|
+
rl.question(chalk.green('\n > '), async (input) => {
|
|
35
|
+
const trimmed = input.trim();
|
|
36
|
+
if (!trimmed || trimmed === '/quit' || trimmed === '/exit') {
|
|
37
|
+
rl.close();
|
|
38
|
+
await shutdownContext();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (trimmed.startsWith('/profile ')) {
|
|
42
|
+
const p = trimmed.split(' ')[1];
|
|
43
|
+
if (['fast', 'balanced', 'precise'].includes(p)) {
|
|
44
|
+
opts.profile = p;
|
|
45
|
+
log.ok(`Profile: ${p}`);
|
|
46
|
+
}
|
|
47
|
+
askLine();
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// Stream answer
|
|
51
|
+
for await (const event of ctx.ragEngine.queryStream({ query: trimmed, profile: opts.profile })) {
|
|
52
|
+
if (event.type === 'chunk')
|
|
53
|
+
process.stdout.write(event.data);
|
|
54
|
+
}
|
|
55
|
+
console.log();
|
|
56
|
+
askLine();
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
// Handle Ctrl+C gracefully in REPL
|
|
60
|
+
rl.on('close', async () => {
|
|
61
|
+
await shutdownContext();
|
|
62
|
+
process.exit(0);
|
|
63
|
+
});
|
|
64
|
+
askLine();
|
|
65
|
+
return; // Don't shutdown -- REPL keeps running
|
|
66
|
+
}
|
|
67
|
+
const ctx = await getContext();
|
|
68
|
+
try {
|
|
69
|
+
if (opts.json) {
|
|
70
|
+
const result = await ctx.ragEngine.query({ query, profile: opts.profile });
|
|
71
|
+
console.log(JSON.stringify(result, null, 2));
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
log.heading('OpenDocuments');
|
|
75
|
+
log.dim(`Profile: ${opts.profile}`);
|
|
76
|
+
log.blank();
|
|
77
|
+
console.log(chalk.green(' >'), chalk.white(query));
|
|
78
|
+
log.blank();
|
|
79
|
+
for await (const event of ctx.ragEngine.queryStream({ query, profile: opts.profile })) {
|
|
80
|
+
if (event.type === 'chunk') {
|
|
81
|
+
process.stdout.write(event.data);
|
|
82
|
+
}
|
|
83
|
+
if (event.type === 'sources' && Array.isArray(event.data) && event.data.length > 0) {
|
|
84
|
+
log.blank();
|
|
85
|
+
log.dim('Sources:');
|
|
86
|
+
for (const src of event.data) {
|
|
87
|
+
log.dim(` ${chalk.cyan(src.sourcePath)}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
log.blank();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
await shutdownContext();
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=ask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask.js","sourceRoot":"","sources":["../../src/commands/ask.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AACxC,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAEnE,MAAM,UAAU,UAAU;IACxB,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC;SACtB,WAAW,CAAC,wCAAwC,CAAC;SACrD,QAAQ,CAAC,SAAS,EAAE,qBAAqB,CAAC;SAC1C,MAAM,CAAC,qBAAqB,EAAE,sCAAsC,EAAE,UAAU,CAAC;SACjF,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC;SAClC,MAAM,CAAC,SAAS,EAAE,iBAAiB,CAAC;SACpC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAC5B,wBAAwB;QACxB,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACvC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAa,EAAE,CAAA;gBAC3B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK;oBAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;gBAC3D,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAA;gBACnE,IAAI,YAAY,EAAE,CAAC;oBACjB,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,iBAAiB,YAAY,EAAE,CAAC,CAAC,CAAC,YAAY,CAAA;gBACxE,CAAC;YACH,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;QACZ,CAAC;QAED,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,wBAAwB;YACxB,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAA;YAC9B,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAA;YAC9C,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;YAErF,GAAG,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAA;YACxC,GAAG,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,OAAO,uBAAuB,CAAC,CAAA;YAExD,MAAM,OAAO,GAAG,GAAG,EAAE;gBACnB,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;oBACjD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;oBAC5B,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;wBAC3D,EAAE,CAAC,KAAK,EAAE,CAAA;wBACV,MAAM,eAAe,EAAE,CAAA;wBACvB,OAAM;oBACR,CAAC;oBACD,IAAI,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;wBACpC,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;wBAC/B,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;4BAChD,IAAI,CAAC,OAAO,GAAG,CAAC,CAAA;4BAChB,GAAG,CAAC,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;wBACzB,CAAC;wBACD,OAAO,EAAE,CAAA;wBACT,OAAM;oBACR,CAAC;oBAED,gBAAgB;oBAChB,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;wBAC/F,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;4BAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAc,CAAC,CAAA;oBACxE,CAAC;oBACD,OAAO,CAAC,GAAG,EAAE,CAAA;oBACb,OAAO,EAAE,CAAA;gBACX,CAAC,CAAC,CAAA;YACJ,CAAC,CAAA;YACD,mCAAmC;YACnC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;gBACxB,MAAM,eAAe,EAAE,CAAA;gBACvB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACjB,CAAC,CAAC,CAAA;YACF,OAAO,EAAE,CAAA;YACT,OAAM,CAAC,uCAAuC;QAChD,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAA;QAC9B,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;gBAC1E,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;YAC9C,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAA;gBAC5B,GAAG,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;gBACnC,GAAG,CAAC,KAAK,EAAE,CAAA;gBACX,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;gBACnD,GAAG,CAAC,KAAK,EAAE,CAAA;gBACX,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;oBACtF,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;wBAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAc,CAAC,CAAA;oBAC5C,CAAC;oBACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAK,KAAK,CAAC,IAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBAC9F,GAAG,CAAC,KAAK,EAAE,CAAA;wBACX,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;wBACnB,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAa,EAAE,CAAC;4BACtC,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAA;wBAC5C,CAAC;oBACH,CAAC;gBACH,CAAC;gBACD,GAAG,CAAC,KAAK,EAAE,CAAA;YACb,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,MAAM,eAAe,EAAE,CAAA;QACzB,CAAC;IACH,CAAC,CAAC,CAAA;AACN,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAKnC,wBAAgB,WAAW,YA+E1B"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { log } from 'opendocuments-core';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { getContext, shutdownContext } from '../utils/bootstrap.js';
|
|
5
|
+
export function authCommand() {
|
|
6
|
+
const cmd = new Command('auth')
|
|
7
|
+
.description('Manage authentication');
|
|
8
|
+
cmd.command('create-key')
|
|
9
|
+
.description('Create a new API key')
|
|
10
|
+
.requiredOption('--name <name>', 'Key name')
|
|
11
|
+
.option('--role <role>', 'Role: admin, member, viewer', 'member')
|
|
12
|
+
.action(async (opts) => {
|
|
13
|
+
const ctx = await getContext();
|
|
14
|
+
try {
|
|
15
|
+
const ws = ctx.workspaceManager.list()[0];
|
|
16
|
+
if (!ws) {
|
|
17
|
+
log.fail('No workspace found');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const { rawKey, record } = ctx.apiKeyManager.create({
|
|
21
|
+
name: opts.name,
|
|
22
|
+
workspaceId: ws.id,
|
|
23
|
+
userId: 'cli-user',
|
|
24
|
+
role: opts.role,
|
|
25
|
+
});
|
|
26
|
+
log.heading('API Key Created');
|
|
27
|
+
log.ok(`Name: ${record.name}`);
|
|
28
|
+
log.ok(`Role: ${record.role}`);
|
|
29
|
+
log.ok(`Key: ${chalk.cyan(rawKey)}`);
|
|
30
|
+
log.blank();
|
|
31
|
+
log.fail('This key will not be shown again. Save it securely.');
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
await shutdownContext();
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
cmd.command('list-keys')
|
|
38
|
+
.description('List API keys')
|
|
39
|
+
.action(async () => {
|
|
40
|
+
const ctx = await getContext();
|
|
41
|
+
try {
|
|
42
|
+
const keys = ctx.apiKeyManager.list();
|
|
43
|
+
if (keys.length === 0) {
|
|
44
|
+
log.info('No API keys');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
log.heading('API Keys');
|
|
48
|
+
for (const key of keys) {
|
|
49
|
+
const lastUsed = key.lastUsedAt ? new Date(key.lastUsedAt).toLocaleDateString() : 'never';
|
|
50
|
+
log.ok(`${key.name.padEnd(20)} ${key.role.padEnd(8)} ${key.keyPrefix}... ${chalk.dim(`last used: ${lastUsed}`)}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
await shutdownContext();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
cmd.command('revoke-key <nameOrId>')
|
|
58
|
+
.description('Revoke an API key by name or ID')
|
|
59
|
+
.action(async (nameOrId) => {
|
|
60
|
+
const ctx = await getContext();
|
|
61
|
+
try {
|
|
62
|
+
const keys = ctx.apiKeyManager.list();
|
|
63
|
+
const key = keys.find(k => k.name === nameOrId || k.id === nameOrId);
|
|
64
|
+
if (!key) {
|
|
65
|
+
log.fail(`Key "${nameOrId}" not found`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
ctx.apiKeyManager.revoke(key.id);
|
|
69
|
+
log.ok(`Key "${nameOrId}" revoked`);
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
await shutdownContext();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
cmd.command('login').description('Login with API key').action(async () => {
|
|
76
|
+
const { input } = await import('@inquirer/prompts');
|
|
77
|
+
const key = await input({ message: 'Enter API key:' });
|
|
78
|
+
const { writeFileSync, mkdirSync } = await import('node:fs');
|
|
79
|
+
const { join } = await import('node:path');
|
|
80
|
+
const dir = join(process.env.HOME || '~', '.opendocuments');
|
|
81
|
+
mkdirSync(dir, { recursive: true });
|
|
82
|
+
writeFileSync(join(dir, 'auth-token'), key);
|
|
83
|
+
log.ok('Logged in. API key saved to ~/.opendocuments/auth-token');
|
|
84
|
+
});
|
|
85
|
+
return cmd;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion.d.ts","sourceRoot":"","sources":["../../src/commands/completion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAKnC,wBAAgB,iBAAiB,YAwChC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { log } from 'opendocuments-core';
|
|
3
|
+
import { writeFileSync, existsSync, readFileSync, appendFileSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
export function completionCommand() {
|
|
6
|
+
const cmd = new Command('completion')
|
|
7
|
+
.description('Install shell completions');
|
|
8
|
+
cmd.command('install')
|
|
9
|
+
.description('Install completions for your shell')
|
|
10
|
+
.option('--shell <shell>', 'Shell: zsh, bash, fish', detectShell())
|
|
11
|
+
.action(async (opts) => {
|
|
12
|
+
const shell = opts.shell;
|
|
13
|
+
if (shell === 'zsh') {
|
|
14
|
+
const completionScript = generateZshCompletion();
|
|
15
|
+
const zshDir = join(process.env.HOME || '~', '.zsh', 'completions');
|
|
16
|
+
const { mkdirSync } = await import('node:fs');
|
|
17
|
+
mkdirSync(zshDir, { recursive: true });
|
|
18
|
+
writeFileSync(join(zshDir, '_opendocuments'), completionScript);
|
|
19
|
+
// Add to .zshrc if not already there
|
|
20
|
+
const zshrc = join(process.env.HOME || '~', '.zshrc');
|
|
21
|
+
const content = existsSync(zshrc) ? readFileSync(zshrc, 'utf-8') : '';
|
|
22
|
+
if (!content.includes('fpath=(~/.zsh/completions')) {
|
|
23
|
+
appendFileSync(zshrc, '\nfpath=(~/.zsh/completions $fpath)\nautoload -Uz compinit && compinit\n');
|
|
24
|
+
}
|
|
25
|
+
log.ok('Zsh completions installed');
|
|
26
|
+
log.arrow('Restart your shell or run: source ~/.zshrc');
|
|
27
|
+
}
|
|
28
|
+
else if (shell === 'bash') {
|
|
29
|
+
const script = generateBashCompletion();
|
|
30
|
+
const bashDir = join(process.env.HOME || '~', '.bash_completions');
|
|
31
|
+
const { mkdirSync } = await import('node:fs');
|
|
32
|
+
mkdirSync(bashDir, { recursive: true });
|
|
33
|
+
writeFileSync(join(bashDir, 'opendocuments'), script);
|
|
34
|
+
log.ok('Bash completions installed');
|
|
35
|
+
log.arrow(`Add to .bashrc: source ~/.bash_completions/opendocuments`);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
log.fail(`Unsupported shell: ${shell}. Supported: zsh, bash`);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
return cmd;
|
|
42
|
+
}
|
|
43
|
+
function detectShell() {
|
|
44
|
+
const shell = process.env.SHELL || '';
|
|
45
|
+
if (shell.includes('zsh'))
|
|
46
|
+
return 'zsh';
|
|
47
|
+
if (shell.includes('bash'))
|
|
48
|
+
return 'bash';
|
|
49
|
+
if (shell.includes('fish'))
|
|
50
|
+
return 'fish';
|
|
51
|
+
return 'zsh';
|
|
52
|
+
}
|
|
53
|
+
function generateZshCompletion() {
|
|
54
|
+
return `#compdef opendocuments
|
|
55
|
+
_opendocuments() {
|
|
56
|
+
local -a commands
|
|
57
|
+
commands=(
|
|
58
|
+
'init:Initialize OpenDocuments project'
|
|
59
|
+
'start:Start the server'
|
|
60
|
+
'stop:Stop the server'
|
|
61
|
+
'ask:Ask a question'
|
|
62
|
+
'search:Search documents'
|
|
63
|
+
'index:Index files'
|
|
64
|
+
'document:Manage documents'
|
|
65
|
+
'connector:Manage connectors'
|
|
66
|
+
'auth:Manage authentication'
|
|
67
|
+
'plugin:Manage plugins'
|
|
68
|
+
'workspace:Manage workspaces'
|
|
69
|
+
'doctor:Health diagnostics'
|
|
70
|
+
'config:View configuration'
|
|
71
|
+
'export:Export data'
|
|
72
|
+
'import:Import data'
|
|
73
|
+
'completion:Shell completions'
|
|
74
|
+
)
|
|
75
|
+
_describe 'command' commands
|
|
76
|
+
}
|
|
77
|
+
compdef _opendocuments opendocuments
|
|
78
|
+
`;
|
|
79
|
+
}
|
|
80
|
+
function generateBashCompletion() {
|
|
81
|
+
return `_opendocuments_completions() {
|
|
82
|
+
local commands="init start stop ask search index document connector auth plugin workspace doctor config export import completion"
|
|
83
|
+
COMPREPLY=($(compgen -W "$commands" -- "\${COMP_WORDS[COMP_CWORD]}"))
|
|
84
|
+
}
|
|
85
|
+
complete -F _opendocuments_completions opendocuments
|
|
86
|
+
`;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=completion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion.js","sourceRoot":"","sources":["../../src/commands/completion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AACjF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,MAAM,UAAU,iBAAiB;IAC/B,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,YAAY,CAAC;SAClC,WAAW,CAAC,2BAA2B,CAAC,CAAA;IAE3C,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC;SACnB,WAAW,CAAC,oCAAoC,CAAC;SACjD,MAAM,CAAC,iBAAiB,EAAE,wBAAwB,EAAE,WAAW,EAAE,CAAC;SAClE,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;QAExB,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;YACpB,MAAM,gBAAgB,GAAG,qBAAqB,EAAE,CAAA;YAChD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,EAAE,MAAM,EAAE,aAAa,CAAC,CAAA;YACnE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAA;YAC7C,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;YACtC,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAAE,gBAAgB,CAAC,CAAA;YAE/D,qCAAqC;YACrC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,EAAE,QAAQ,CAAC,CAAA;YACrD,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;YACrE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAC,EAAE,CAAC;gBACnD,cAAc,CAAC,KAAK,EAAE,0EAA0E,CAAC,CAAA;YACnG,CAAC;YAED,GAAG,CAAC,EAAE,CAAC,2BAA2B,CAAC,CAAA;YACnC,GAAG,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAA;QACzD,CAAC;aAAM,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;YAC5B,MAAM,MAAM,GAAG,sBAAsB,EAAE,CAAA;YACvC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,EAAE,mBAAmB,CAAC,CAAA;YAClE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAA;YAC7C,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;YACvC,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,EAAE,MAAM,CAAC,CAAA;YACrD,GAAG,CAAC,EAAE,CAAC,4BAA4B,CAAC,CAAA;YACpC,GAAG,CAAC,KAAK,CAAC,0DAA0D,CAAC,CAAA;QACvE,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,IAAI,CAAC,sBAAsB,KAAK,wBAAwB,CAAC,CAAA;QAC/D,CAAC;IACH,CAAC,CAAC,CAAA;IAEJ,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,SAAS,WAAW;IAClB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAA;IACrC,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACvC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAA;IACzC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAA;IACzC,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,qBAAqB;IAC5B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;CAwBR,CAAA;AACD,CAAC;AAED,SAAS,sBAAsB;IAC7B,OAAO;;;;;CAKR,CAAA;AACD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-cmd.d.ts","sourceRoot":"","sources":["../../src/commands/config-cmd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAKnC,wBAAgB,aAAa,YAoD5B"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { log, loadConfig } from 'opendocuments-core';
|
|
3
|
+
import { existsSync, unlinkSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
export function configCommand() {
|
|
6
|
+
const cmd = new Command('config')
|
|
7
|
+
.description('View or modify configuration');
|
|
8
|
+
cmd.command('show')
|
|
9
|
+
.description('Show full config')
|
|
10
|
+
.argument('[key]', 'Config key to view')
|
|
11
|
+
.action(async (key) => {
|
|
12
|
+
const config = loadConfig(process.cwd());
|
|
13
|
+
if (!key) {
|
|
14
|
+
log.heading('Configuration');
|
|
15
|
+
console.log(JSON.stringify(config, null, 2));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const keys = key.split('.');
|
|
19
|
+
let current = config;
|
|
20
|
+
for (const k of keys) {
|
|
21
|
+
current = current?.[k];
|
|
22
|
+
}
|
|
23
|
+
if (current === undefined)
|
|
24
|
+
log.fail(`Config key not found: ${key}`);
|
|
25
|
+
else
|
|
26
|
+
console.log(JSON.stringify(current, null, 2));
|
|
27
|
+
});
|
|
28
|
+
cmd.command('edit')
|
|
29
|
+
.description('Open config file in editor')
|
|
30
|
+
.action(async () => {
|
|
31
|
+
const editor = process.env.EDITOR || 'vi';
|
|
32
|
+
const configPath = join(process.cwd(), 'opendocuments.config.ts');
|
|
33
|
+
if (!existsSync(configPath)) {
|
|
34
|
+
log.fail('No config file found. Run: opendocuments init');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const { execSync } = await import('node:child_process');
|
|
38
|
+
execSync(`${editor} ${configPath}`, { stdio: 'inherit' });
|
|
39
|
+
});
|
|
40
|
+
cmd.command('reset')
|
|
41
|
+
.description('Reset config to defaults')
|
|
42
|
+
.action(async () => {
|
|
43
|
+
const configPath = join(process.cwd(), 'opendocuments.config.ts');
|
|
44
|
+
if (existsSync(configPath)) {
|
|
45
|
+
const { confirm } = await import('@inquirer/prompts');
|
|
46
|
+
const yes = await confirm({ message: 'Reset configuration to defaults?' });
|
|
47
|
+
if (!yes)
|
|
48
|
+
return;
|
|
49
|
+
unlinkSync(configPath);
|
|
50
|
+
}
|
|
51
|
+
log.ok('Configuration reset. Run: opendocuments init');
|
|
52
|
+
});
|
|
53
|
+
// Default action (no subcommand) -- show config
|
|
54
|
+
cmd.action(async () => {
|
|
55
|
+
const config = loadConfig(process.cwd());
|
|
56
|
+
log.heading('Configuration');
|
|
57
|
+
console.log(JSON.stringify(config, null, 2));
|
|
58
|
+
});
|
|
59
|
+
return cmd;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=config-cmd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-cmd.js","sourceRoot":"","sources":["../../src/commands/config-cmd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AACpD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,MAAM,UAAU,aAAa;IAC3B,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;SAC9B,WAAW,CAAC,8BAA8B,CAAC,CAAA;IAE9C,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;SAChB,WAAW,CAAC,kBAAkB,CAAC;SAC/B,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;SACvC,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACpB,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;QACxC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAA;YAC5B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;YAC5C,OAAM;QACR,CAAC;QACD,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC3B,IAAI,OAAO,GAAQ,MAAM,CAAA;QACzB,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YAAC,OAAO,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;QAAC,CAAC;QAChD,IAAI,OAAO,KAAK,SAAS;YAAE,GAAG,CAAC,IAAI,CAAC,yBAAyB,GAAG,EAAE,CAAC,CAAA;;YAC9D,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;IACpD,CAAC,CAAC,CAAA;IAEJ,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;SAChB,WAAW,CAAC,4BAA4B,CAAC;SACzC,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,IAAI,CAAA;QACzC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,yBAAyB,CAAC,CAAA;QACjE,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAAC,GAAG,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;YAAC,OAAM;QAAC,CAAC;QAClG,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAA;QACvD,QAAQ,CAAC,GAAG,MAAM,IAAI,UAAU,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAA;IAC3D,CAAC,CAAC,CAAA;IAEJ,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC;SACjB,WAAW,CAAC,0BAA0B,CAAC;SACvC,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,yBAAyB,CAAC,CAAA;QACjE,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC3B,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAA;YACrD,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,EAAE,OAAO,EAAE,kCAAkC,EAAE,CAAC,CAAA;YAC1E,IAAI,CAAC,GAAG;gBAAE,OAAM;YAChB,UAAU,CAAC,UAAU,CAAC,CAAA;QACxB,CAAC;QACD,GAAG,CAAC,EAAE,CAAC,8CAA8C,CAAC,CAAA;IACxD,CAAC,CAAC,CAAA;IAEJ,gDAAgD;IAChD,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE;QACpB,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;QACxC,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAA;QAC5B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;IAEF,OAAO,GAAG,CAAA;AACZ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connector.d.ts","sourceRoot":"","sources":["../../src/commands/connector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAKnC,wBAAgB,gBAAgB,YA2E/B"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { log } from 'opendocuments-core';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { getContext, shutdownContext } from '../utils/bootstrap.js';
|
|
5
|
+
export function connectorCommand() {
|
|
6
|
+
const cmd = new Command('connector')
|
|
7
|
+
.description('Manage connectors');
|
|
8
|
+
cmd.command('list')
|
|
9
|
+
.description('List registered connectors')
|
|
10
|
+
.action(async () => {
|
|
11
|
+
const ctx = await getContext();
|
|
12
|
+
try {
|
|
13
|
+
const connectors = ctx.connectorManager.listConnectors();
|
|
14
|
+
if (connectors.length === 0) {
|
|
15
|
+
log.info('No connectors registered');
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
log.heading('Connectors');
|
|
19
|
+
for (const c of connectors) {
|
|
20
|
+
const syncInfo = c.lastSyncedAt ? `last sync: ${c.lastSyncedAt}` : 'never synced';
|
|
21
|
+
log.ok(`${c.name.padEnd(40)} ${chalk.dim(syncInfo)}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
await shutdownContext();
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
cmd.command('sync [name]')
|
|
29
|
+
.description('Sync a connector (or all)')
|
|
30
|
+
.action(async (name) => {
|
|
31
|
+
const ctx = await getContext();
|
|
32
|
+
try {
|
|
33
|
+
if (name) {
|
|
34
|
+
log.wait(`Syncing ${name}...`);
|
|
35
|
+
const result = await ctx.connectorManager.syncConnector(name);
|
|
36
|
+
log.ok(`Discovered: ${result.documentsDiscovered}, Indexed: ${result.documentsIndexed}, Skipped: ${result.documentsSkipped}`);
|
|
37
|
+
if (result.errors.length > 0) {
|
|
38
|
+
for (const err of result.errors)
|
|
39
|
+
log.fail(err);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
log.wait('Syncing all connectors...');
|
|
44
|
+
const results = await ctx.connectorManager.syncAll();
|
|
45
|
+
for (const r of results) {
|
|
46
|
+
log.ok(`${r.connectorName}: ${r.documentsIndexed} indexed, ${r.documentsSkipped} skipped`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
await shutdownContext();
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
cmd.command('add <type>').description('Add a connector (configures in opendocuments.config.ts)').action(async (type) => {
|
|
55
|
+
log.info(`To add a ${type} connector, edit opendocuments.config.ts:`);
|
|
56
|
+
log.arrow(`connectors: [{ type: '${type}', ... }]`);
|
|
57
|
+
});
|
|
58
|
+
cmd.command('status').description('Show connector sync status').action(async () => {
|
|
59
|
+
const ctx = await getContext();
|
|
60
|
+
try {
|
|
61
|
+
const connectors = ctx.connectorManager.listConnectors();
|
|
62
|
+
if (connectors.length === 0) {
|
|
63
|
+
log.info('No connectors configured');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
log.heading('Connector Status');
|
|
67
|
+
for (const c of connectors) {
|
|
68
|
+
const statusIcon = c.status === 'active' ? chalk.green('[ok]') : chalk.red('[!!]');
|
|
69
|
+
log.dim(` ${statusIcon} ${c.name.padEnd(30)} last sync: ${c.lastSyncedAt || 'never'}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
await shutdownContext();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
cmd.command('remove <name>').description('Remove a connector').action(async (name) => {
|
|
77
|
+
log.info(`To remove the ${name} connector, edit opendocuments.config.ts and remove it from the connectors array.`);
|
|
78
|
+
});
|
|
79
|
+
cmd.command('auth <name>').description('Re-authenticate a connector').action(async (name) => {
|
|
80
|
+
log.info(`To update credentials for ${name}, edit opendocuments.config.ts or update environment variables.`);
|
|
81
|
+
});
|
|
82
|
+
return cmd;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=connector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connector.js","sourceRoot":"","sources":["../../src/commands/connector.ts"],"names":[],"mappings":"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"}
|