@hiai-gg/docsmint 0.3.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 +171 -0
- package/README.md +348 -0
- package/backend/src/lib/logger.ts +18 -0
- package/backend/src/lib/redis-factory.ts +40 -0
- package/backend/src/lib/storage-factory.ts +56 -0
- package/frontend/src/lib/components/editor/shared-document.ts +237 -0
- package/frontend/src/lib/extensions/context.ts +60 -0
- package/frontend/src/lib/extensions/doc-tabs.ts +18 -0
- package/frontend/src/lib/extensions/resolve.ts +48 -0
- package/frontend/src/lib/extensions/types.ts +202 -0
- package/frontend/src/lib/hosts/DocsmintSharedDocumentHost.svelte +65 -0
- package/frontend/src/lib/hosts/HiaiDocsDashboardHost.svelte +1007 -0
- package/frontend/src/lib/hosts/HiaiDocsExtensionProvider.svelte +20 -0
- package/frontend/src/lib/hosts/HiaiDocsSearchHost.svelte +996 -0
- package/frontend/src/lib/hosts/index.ts +25 -0
- package/frontend/src/lib/index.ts +65 -0
- package/frontend/src/lib/stores/doc-tab-registry.svelte.ts +68 -0
- package/package.json +178 -0
- package/packages/cli/src/client.ts +271 -0
- package/packages/cli/src/commands/config.ts +47 -0
- package/packages/cli/src/commands/create.ts +35 -0
- package/packages/cli/src/commands/delete.ts +37 -0
- package/packages/cli/src/commands/export.ts +36 -0
- package/packages/cli/src/commands/folders.ts +88 -0
- package/packages/cli/src/commands/history.ts +55 -0
- package/packages/cli/src/commands/list.ts +61 -0
- package/packages/cli/src/commands/read.ts +38 -0
- package/packages/cli/src/commands/restore.ts +30 -0
- package/packages/cli/src/commands/search.ts +56 -0
- package/packages/cli/src/commands/snapshot.ts +35 -0
- package/packages/cli/src/commands/update.ts +54 -0
- package/packages/cli/src/config.ts +83 -0
- package/packages/cli/src/format.ts +153 -0
- package/packages/cli/src/index.ts +73 -0
- package/packages/db/src/client.ts +20 -0
- package/packages/db/src/index.ts +5 -0
- package/packages/db/src/schema.ts +692 -0
- package/packages/db/src/with-tenant.ts +75 -0
- package/packages/mcp-server/src/client.ts +172 -0
- package/packages/mcp-server/src/index.ts +109 -0
- package/packages/mcp-server/src/tools/create-document.ts +32 -0
- package/packages/mcp-server/src/tools/create-folder.ts +24 -0
- package/packages/mcp-server/src/tools/create-snapshot.ts +30 -0
- package/packages/mcp-server/src/tools/export-document.ts +22 -0
- package/packages/mcp-server/src/tools/get-document.ts +20 -0
- package/packages/mcp-server/src/tools/list-documents.ts +42 -0
- package/packages/mcp-server/src/tools/list-folders.ts +25 -0
- package/packages/mcp-server/src/tools/search.ts +42 -0
- package/packages/mcp-server/src/tools/update-document.ts +30 -0
- package/packages/mcp-server/src/tools/version-history.ts +32 -0
- package/packages/mcp-server/src/types.ts +126 -0
- package/packages/sdk/dist/client.d.ts +187 -0
- package/packages/sdk/dist/client.js +568 -0
- package/packages/sdk/dist/index.d.ts +3 -0
- package/packages/sdk/dist/index.js +1 -0
- package/packages/sdk/dist/types.d.ts +391 -0
- package/packages/sdk/dist/types.js +8 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public application hosts.
|
|
3
|
+
*
|
|
4
|
+
* These components preserve the standalone HiAi-Docs routes while allowing a
|
|
5
|
+
* product build to mount typed, additive frontend extensions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { default as DocsmintSharedDocumentHost } from "./DocsmintSharedDocumentHost.svelte";
|
|
9
|
+
export type { HiaiDocsDashboardData } from "./HiaiDocsDashboardHost.svelte";
|
|
10
|
+
/** @deprecated Use DocsmintDashboardHost. */
|
|
11
|
+
export {
|
|
12
|
+
default as HiaiDocsDashboardHost,
|
|
13
|
+
default as DocsmintDashboardHost,
|
|
14
|
+
} from "./HiaiDocsDashboardHost.svelte";
|
|
15
|
+
/** Canonical extension provider. */
|
|
16
|
+
export {
|
|
17
|
+
default as HiaiDocsExtensionProvider,
|
|
18
|
+
default as DocsmintExtensionProvider,
|
|
19
|
+
} from "./HiaiDocsExtensionProvider.svelte";
|
|
20
|
+
export type { HiaiDocsSearchData } from "./HiaiDocsSearchHost.svelte";
|
|
21
|
+
/** @deprecated Use DocsmintSearchHost. */
|
|
22
|
+
export {
|
|
23
|
+
default as HiaiDocsSearchHost,
|
|
24
|
+
default as DocsmintSearchHost,
|
|
25
|
+
} from "./HiaiDocsSearchHost.svelte";
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public entrypoint for the hiai-docs frontend extension package.
|
|
3
|
+
*
|
|
4
|
+
* Keep this barrel limited to SSR-safe contracts and pure helpers. Hosts such
|
|
5
|
+
* as DocsMint import this path from the published package; route modules and
|
|
6
|
+
* mutable app singletons are intentionally not part of the public surface.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type {
|
|
10
|
+
ProseMirrorDoc,
|
|
11
|
+
ProseMirrorNode,
|
|
12
|
+
SharedAttachmentObjectUrls,
|
|
13
|
+
} from "./components/editor/shared-document";
|
|
14
|
+
export {
|
|
15
|
+
hydrateSharedAttachmentImages,
|
|
16
|
+
renderSharedDocument,
|
|
17
|
+
sharedAttachmentHeaders,
|
|
18
|
+
} from "./components/editor/shared-document";
|
|
19
|
+
export {
|
|
20
|
+
createFrontendExtensions,
|
|
21
|
+
getFrontendExtensions,
|
|
22
|
+
getHiaiDocsExtensions,
|
|
23
|
+
provideFrontendExtensions,
|
|
24
|
+
setFrontendExtensions,
|
|
25
|
+
setHiaiDocsExtensions,
|
|
26
|
+
} from "./extensions/context";
|
|
27
|
+
export type {
|
|
28
|
+
DocTabDefinition,
|
|
29
|
+
DocTabIcon,
|
|
30
|
+
DocTabPanelProps,
|
|
31
|
+
} from "./extensions/doc-tabs";
|
|
32
|
+
export type {
|
|
33
|
+
CommandPaletteAction,
|
|
34
|
+
CommandPaletteActionContext,
|
|
35
|
+
CommandPaletteActionExtension,
|
|
36
|
+
DashboardWidgetExtension,
|
|
37
|
+
DashboardWidgetProps,
|
|
38
|
+
DocumentMenuAction,
|
|
39
|
+
DocumentMenuActionContext,
|
|
40
|
+
DocumentMenuActionExtension,
|
|
41
|
+
EditorActionContext,
|
|
42
|
+
EditorActionExtension,
|
|
43
|
+
ExtensionAction,
|
|
44
|
+
ExtensionIcon,
|
|
45
|
+
ExtensionVisibility,
|
|
46
|
+
ExtensionVisibilityContext,
|
|
47
|
+
FrontendExtensions,
|
|
48
|
+
HiaiDocsFrontendExtensions,
|
|
49
|
+
NavigationExtension,
|
|
50
|
+
SearchWidgetExtension,
|
|
51
|
+
SearchWidgetProps,
|
|
52
|
+
SettingsSectionExtension,
|
|
53
|
+
SettingsSectionProps,
|
|
54
|
+
SharedDocumentExtension,
|
|
55
|
+
SharedDocumentExtensionContext,
|
|
56
|
+
} from "./extensions/types";
|
|
57
|
+
export {
|
|
58
|
+
DocsmintDashboardHost,
|
|
59
|
+
DocsmintExtensionProvider,
|
|
60
|
+
DocsmintSearchHost,
|
|
61
|
+
DocsmintSharedDocumentHost,
|
|
62
|
+
HiaiDocsDashboardHost,
|
|
63
|
+
HiaiDocsExtensionProvider,
|
|
64
|
+
HiaiDocsSearchHost,
|
|
65
|
+
} from "./hosts";
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* doc-tab-registry.svelte.ts
|
|
3
|
+
*
|
|
4
|
+
* Lightweight open registry for document-page tabs.
|
|
5
|
+
*
|
|
6
|
+
* hiai-docs ships this file empty - no tabs are registered out of the box.
|
|
7
|
+
* External projects (e.g. hiai-admin, commercial forks) call registerDocTab()
|
|
8
|
+
* from their own +layout.svelte to inject custom tabs alongside the built-in
|
|
9
|
+
* editor without modifying any hiai-docs core files.
|
|
10
|
+
*
|
|
11
|
+
* Usage in an external project's layout:
|
|
12
|
+
* import { registerDocTab } from "$lib/stores/doc-tab-registry.svelte";
|
|
13
|
+
* import HtmlRenditionPanel from "./HtmlRenditionPanel.svelte";
|
|
14
|
+
* registerDocTab({ id: "html-rendition", label: "HTML Preview", component: HtmlRenditionPanel });
|
|
15
|
+
*
|
|
16
|
+
* STABILITY NOTICE:
|
|
17
|
+
* The interfaces `DocTabPanelProps` and `DocTabDefinition` and functions/states
|
|
18
|
+
* `registerDocTab` and `docTabRegistry` are considered stable public APIs.
|
|
19
|
+
* Breaking changes to these will be announced as major version bumps.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type { DocTabDefinition } from "../extensions/doc-tabs";
|
|
23
|
+
|
|
24
|
+
export type {
|
|
25
|
+
DocTabDefinition,
|
|
26
|
+
DocTabIcon,
|
|
27
|
+
DocTabPanelProps,
|
|
28
|
+
} from "../extensions/doc-tabs";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Reactive array of registered doc tabs.
|
|
32
|
+
* Read by the document page to render the tab bar and panels.
|
|
33
|
+
* Mutate only via registerDocTab() to guarantee idempotency.
|
|
34
|
+
*/
|
|
35
|
+
export const docTabRegistry: DocTabDefinition[] = $state([]);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Create an isolated tab collection for a host-provided extension manifest.
|
|
39
|
+
* The legacy `docTabRegistry` export remains available for existing clients,
|
|
40
|
+
* while new app-shell integrations should keep their tabs request-scoped.
|
|
41
|
+
*/
|
|
42
|
+
export function createDocTabRegistry(
|
|
43
|
+
initial: readonly DocTabDefinition[] = [],
|
|
44
|
+
): DocTabDefinition[] {
|
|
45
|
+
return [...initial];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Register a tab in an isolated collection, preserving idempotency. */
|
|
49
|
+
export function registerDocTabIn(
|
|
50
|
+
registry: DocTabDefinition[],
|
|
51
|
+
tab: DocTabDefinition,
|
|
52
|
+
): void {
|
|
53
|
+
if (!registry.find((existing) => existing.id === tab.id)) {
|
|
54
|
+
registry.push(tab);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Register a custom document tab.
|
|
60
|
+
*
|
|
61
|
+
* Safe to call multiple times (e.g. across HMR reloads) - duplicate ids
|
|
62
|
+
* are silently ignored so layout-level registrations do not stack up.
|
|
63
|
+
*
|
|
64
|
+
* @param tab - Tab definition to register.
|
|
65
|
+
*/
|
|
66
|
+
export function registerDocTab(tab: DocTabDefinition): void {
|
|
67
|
+
registerDocTabIn(docTabRegistry, tab);
|
|
68
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hiai-gg/docsmint",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"description": "The lightweight, AI-native self-hosted knowledge base — built-in RAG, hybrid semantic search, and a clean REST API for AI agents.",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/HiAi-gg/docsmint"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/hiai-gg/docsmint#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/hiai-gg/docsmint/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"knowledge-base",
|
|
17
|
+
"ai-native",
|
|
18
|
+
"wiki",
|
|
19
|
+
"markdown",
|
|
20
|
+
"ai-embeddings",
|
|
21
|
+
"rag",
|
|
22
|
+
"semantic-search",
|
|
23
|
+
"self-hosted",
|
|
24
|
+
"pgvector",
|
|
25
|
+
"ollama",
|
|
26
|
+
"local-llm",
|
|
27
|
+
"pwa",
|
|
28
|
+
"progressive-web-app",
|
|
29
|
+
"offline-knowledge-base",
|
|
30
|
+
"docsmint",
|
|
31
|
+
"outline-alternative",
|
|
32
|
+
"docmost-alternative"
|
|
33
|
+
],
|
|
34
|
+
"main": "./packages/sdk/dist/index.js",
|
|
35
|
+
"types": "./packages/sdk/dist/index.d.ts",
|
|
36
|
+
"exports": {
|
|
37
|
+
".": {
|
|
38
|
+
"import": "./packages/sdk/dist/index.js",
|
|
39
|
+
"types": "./packages/sdk/dist/index.d.ts"
|
|
40
|
+
},
|
|
41
|
+
"./sdk": {
|
|
42
|
+
"import": "./packages/sdk/dist/index.js",
|
|
43
|
+
"types": "./packages/sdk/dist/index.d.ts"
|
|
44
|
+
},
|
|
45
|
+
"./frontend": {
|
|
46
|
+
"import": "./frontend/src/lib/index.ts",
|
|
47
|
+
"types": "./frontend/src/lib/index.ts"
|
|
48
|
+
},
|
|
49
|
+
"./frontend/shared-document": {
|
|
50
|
+
"import": "./frontend/src/lib/components/editor/shared-document.ts",
|
|
51
|
+
"types": "./frontend/src/lib/components/editor/shared-document.ts"
|
|
52
|
+
},
|
|
53
|
+
"./frontend/extensions": {
|
|
54
|
+
"import": "./frontend/src/lib/extensions/types.ts",
|
|
55
|
+
"types": "./frontend/src/lib/extensions/types.ts"
|
|
56
|
+
},
|
|
57
|
+
"./frontend/hosts": {
|
|
58
|
+
"import": "./frontend/src/lib/hosts/index.ts",
|
|
59
|
+
"types": "./frontend/src/lib/hosts/index.ts"
|
|
60
|
+
},
|
|
61
|
+
"./frontend/legacy/doc-tab-registry": {
|
|
62
|
+
"import": "./frontend/src/lib/stores/doc-tab-registry.svelte.ts",
|
|
63
|
+
"types": "./frontend/src/lib/stores/doc-tab-registry.svelte.ts"
|
|
64
|
+
},
|
|
65
|
+
"./schema": {
|
|
66
|
+
"import": "./packages/db/src/schema.ts",
|
|
67
|
+
"types": "./packages/db/src/schema.ts"
|
|
68
|
+
},
|
|
69
|
+
"./db": {
|
|
70
|
+
"import": "./packages/db/src/index.ts",
|
|
71
|
+
"types": "./packages/db/src/index.ts"
|
|
72
|
+
},
|
|
73
|
+
"./db/client": {
|
|
74
|
+
"import": "./packages/db/src/client.ts",
|
|
75
|
+
"types": "./packages/db/src/client.ts"
|
|
76
|
+
},
|
|
77
|
+
"./db/with-tenant": {
|
|
78
|
+
"import": "./packages/db/src/with-tenant.ts",
|
|
79
|
+
"types": "./packages/db/src/with-tenant.ts"
|
|
80
|
+
},
|
|
81
|
+
"./backend/lib/redis": {
|
|
82
|
+
"import": "./backend/src/lib/redis-factory.ts",
|
|
83
|
+
"types": "./backend/src/lib/redis-factory.ts"
|
|
84
|
+
},
|
|
85
|
+
"./backend/lib/storage": {
|
|
86
|
+
"import": "./backend/src/lib/storage-factory.ts",
|
|
87
|
+
"types": "./backend/src/lib/storage-factory.ts"
|
|
88
|
+
},
|
|
89
|
+
"./backend/lib/logger": {
|
|
90
|
+
"import": "./backend/src/lib/logger.ts",
|
|
91
|
+
"types": "./backend/src/lib/logger.ts"
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
"bin": {
|
|
95
|
+
"docsmint": "./packages/cli/src/index.ts",
|
|
96
|
+
"docsmint-mcp": "./packages/mcp-server/src/index.ts",
|
|
97
|
+
"hiai-docs": "./packages/cli/src/index.ts",
|
|
98
|
+
"hiai-docs-mcp": "./packages/mcp-server/src/index.ts"
|
|
99
|
+
},
|
|
100
|
+
"files": [
|
|
101
|
+
"packages/sdk/dist/index.js",
|
|
102
|
+
"packages/sdk/dist/index.d.ts",
|
|
103
|
+
"packages/sdk/dist/client.js",
|
|
104
|
+
"packages/sdk/dist/client.d.ts",
|
|
105
|
+
"packages/sdk/dist/types.js",
|
|
106
|
+
"packages/sdk/dist/types.d.ts",
|
|
107
|
+
"packages/db/src/index.ts",
|
|
108
|
+
"packages/db/src/client.ts",
|
|
109
|
+
"packages/db/src/schema.ts",
|
|
110
|
+
"packages/db/src/with-tenant.ts",
|
|
111
|
+
"packages/cli/src/index.ts",
|
|
112
|
+
"packages/cli/src/client.ts",
|
|
113
|
+
"packages/cli/src/config.ts",
|
|
114
|
+
"packages/cli/src/format.ts",
|
|
115
|
+
"packages/cli/src/commands",
|
|
116
|
+
"packages/mcp-server/src/index.ts",
|
|
117
|
+
"packages/mcp-server/src/client.ts",
|
|
118
|
+
"packages/mcp-server/src/types.ts",
|
|
119
|
+
"packages/mcp-server/src/tools",
|
|
120
|
+
"backend/src/lib/redis-factory.ts",
|
|
121
|
+
"backend/src/lib/storage-factory.ts",
|
|
122
|
+
"backend/src/lib/logger.ts",
|
|
123
|
+
"frontend/src/lib/index.ts",
|
|
124
|
+
"frontend/src/lib/extensions/context.ts",
|
|
125
|
+
"frontend/src/lib/extensions/doc-tabs.ts",
|
|
126
|
+
"frontend/src/lib/extensions/resolve.ts",
|
|
127
|
+
"frontend/src/lib/extensions/types.ts",
|
|
128
|
+
"frontend/src/lib/hosts",
|
|
129
|
+
"frontend/src/lib/stores/doc-tab-registry.svelte.ts",
|
|
130
|
+
"frontend/src/lib/components/editor/shared-document.ts",
|
|
131
|
+
"README.md",
|
|
132
|
+
"LICENSE"
|
|
133
|
+
],
|
|
134
|
+
"engines": {
|
|
135
|
+
"bun": ">= 1.3.14"
|
|
136
|
+
},
|
|
137
|
+
"publishConfig": {
|
|
138
|
+
"access": "public"
|
|
139
|
+
},
|
|
140
|
+
"scripts": {
|
|
141
|
+
"dev": "docker compose up -d",
|
|
142
|
+
"build": "docker compose build",
|
|
143
|
+
"start": "docker compose up -d",
|
|
144
|
+
"stop": "docker compose down",
|
|
145
|
+
"build:sdk": "cd packages/sdk && bun run build",
|
|
146
|
+
"prepublishOnly": "cd packages/sdk && bun run build"
|
|
147
|
+
},
|
|
148
|
+
"dependencies": {
|
|
149
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
150
|
+
"commander": "^13.1.0",
|
|
151
|
+
"zod": "^4.0.0"
|
|
152
|
+
},
|
|
153
|
+
"peerDependencies": {
|
|
154
|
+
"drizzle-orm": "^0.45.2",
|
|
155
|
+
"postgres": "^3.4.9",
|
|
156
|
+
"ioredis": "^5.4.0",
|
|
157
|
+
"@aws-sdk/client-s3": "^3.0.0",
|
|
158
|
+
"pino": "^10.0.0",
|
|
159
|
+
"svelte": "^5.56.4"
|
|
160
|
+
},
|
|
161
|
+
"peerDependenciesMeta": {
|
|
162
|
+
"drizzle-orm": {
|
|
163
|
+
"optional": true
|
|
164
|
+
},
|
|
165
|
+
"postgres": {
|
|
166
|
+
"optional": true
|
|
167
|
+
},
|
|
168
|
+
"ioredis": {
|
|
169
|
+
"optional": true
|
|
170
|
+
},
|
|
171
|
+
"@aws-sdk/client-s3": {
|
|
172
|
+
"optional": true
|
|
173
|
+
},
|
|
174
|
+
"pino": {
|
|
175
|
+
"optional": true
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REST client for hiai-docs.
|
|
3
|
+
*
|
|
4
|
+
* Pattern mirrors @hiai-docs/mcp-server/src/client.ts: one
|
|
5
|
+
* `request()` helper handles auth headers, query encoding, and
|
|
6
|
+
* error unwrapping; the exported `client` object groups all
|
|
7
|
+
* endpoints into typed methods.
|
|
8
|
+
*
|
|
9
|
+
* Configuration comes from `./config.ts` (file + env), which
|
|
10
|
+
* differs from the MCP server's env-only approach because the CLI
|
|
11
|
+
* is interactive and benefits from a persistent config file.
|
|
12
|
+
*
|
|
13
|
+
* Bun-native. Uses global `fetch`. Throws `HiaiDocsError` on
|
|
14
|
+
* non-2xx responses with the error message extracted from the
|
|
15
|
+
* response body when possible.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { loadConfig, type Config } from "./config.js";
|
|
19
|
+
|
|
20
|
+
export class HiaiDocsError extends Error {
|
|
21
|
+
constructor(
|
|
22
|
+
message: string,
|
|
23
|
+
readonly status: number,
|
|
24
|
+
readonly body: unknown,
|
|
25
|
+
) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = "HiaiDocsError";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface RequestOptions {
|
|
32
|
+
query?: Record<string, string | number | boolean | string[] | undefined>;
|
|
33
|
+
body?: unknown;
|
|
34
|
+
headers?: Record<string, string>;
|
|
35
|
+
accept?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function request<T>(
|
|
39
|
+
method: string,
|
|
40
|
+
path: string,
|
|
41
|
+
options: RequestOptions = {},
|
|
42
|
+
): Promise<T> {
|
|
43
|
+
const cfg = loadConfig();
|
|
44
|
+
const baseUrl = cfg.url.replace(/\/+$/, "");
|
|
45
|
+
const url = new URL(path.startsWith("/") ? path : `/${path}`, `${baseUrl}/`);
|
|
46
|
+
|
|
47
|
+
if (options.query) {
|
|
48
|
+
for (const [key, value] of Object.entries(options.query)) {
|
|
49
|
+
if (value === undefined) continue;
|
|
50
|
+
if (Array.isArray(value)) {
|
|
51
|
+
if (value.length > 0) url.searchParams.set(key, value.join(","));
|
|
52
|
+
} else {
|
|
53
|
+
url.searchParams.set(key, String(value));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const headers: Record<string, string> = {
|
|
59
|
+
Accept: options.accept ?? "application/json",
|
|
60
|
+
...options.headers,
|
|
61
|
+
};
|
|
62
|
+
if (cfg.apiKey) {
|
|
63
|
+
headers["Authorization"] = `Bearer ${cfg.apiKey}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let body: BodyInit | undefined;
|
|
67
|
+
if (options.body !== undefined) {
|
|
68
|
+
headers["Content-Type"] = "application/json";
|
|
69
|
+
body = JSON.stringify(options.body);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const response = await fetch(url, { method, headers, body });
|
|
73
|
+
|
|
74
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
75
|
+
const isJson = contentType.includes("application/json");
|
|
76
|
+
const payload: unknown = isJson
|
|
77
|
+
? await response.json().catch(() => null)
|
|
78
|
+
: await response.text().catch(() => null);
|
|
79
|
+
|
|
80
|
+
if (!response.ok) {
|
|
81
|
+
const message =
|
|
82
|
+
(isJson &&
|
|
83
|
+
payload &&
|
|
84
|
+
typeof payload === "object" &&
|
|
85
|
+
"error" in payload
|
|
86
|
+
? String((payload as { error: unknown }).error)
|
|
87
|
+
: typeof payload === "string" && payload.length > 0
|
|
88
|
+
? payload
|
|
89
|
+
: `HTTP ${response.status} ${response.statusText}`) ||
|
|
90
|
+
`HTTP ${response.status}`;
|
|
91
|
+
throw new HiaiDocsError(message, response.status, payload);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return payload as T;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function joinId(...segments: string[]): string {
|
|
98
|
+
return segments.map((s) => encodeURIComponent(s)).join("/");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// --- Response types ----------------------------------------------------------
|
|
102
|
+
|
|
103
|
+
export interface DocumentSummary {
|
|
104
|
+
id: string;
|
|
105
|
+
title: string;
|
|
106
|
+
content?: string | null;
|
|
107
|
+
folderId?: string | null;
|
|
108
|
+
folderName?: string | null;
|
|
109
|
+
createdAt: string;
|
|
110
|
+
updatedAt: string;
|
|
111
|
+
tags?: Array<{ id: string; name: string; color: string | null }>;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface DocumentDetail extends DocumentSummary {
|
|
115
|
+
ownerId: string;
|
|
116
|
+
content?: string | null;
|
|
117
|
+
contentJson?: unknown;
|
|
118
|
+
metadata?: unknown;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface Folder {
|
|
122
|
+
id: string;
|
|
123
|
+
ownerId?: string;
|
|
124
|
+
name: string;
|
|
125
|
+
parentId?: string | null;
|
|
126
|
+
createdAt?: string;
|
|
127
|
+
updatedAt?: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface VersionRow {
|
|
131
|
+
id: string;
|
|
132
|
+
documentId: string;
|
|
133
|
+
createdBy: string;
|
|
134
|
+
createdAt: string;
|
|
135
|
+
label?: string | null;
|
|
136
|
+
description?: string | null;
|
|
137
|
+
isSnapshot: boolean;
|
|
138
|
+
restoredFrom?: string | null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface SnapshotRow extends VersionRow {
|
|
142
|
+
content?: string | null;
|
|
143
|
+
contentJson?: unknown;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface SearchItem {
|
|
147
|
+
id: string;
|
|
148
|
+
title: string;
|
|
149
|
+
snippet: string;
|
|
150
|
+
score: number;
|
|
151
|
+
folder_id?: string | null;
|
|
152
|
+
folder_name?: string | null;
|
|
153
|
+
created_at: string;
|
|
154
|
+
updated_at: string;
|
|
155
|
+
tags?: Array<{ id: string; name: string; color: string | null }>;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface SearchResponse {
|
|
159
|
+
items: SearchItem[];
|
|
160
|
+
total: number;
|
|
161
|
+
page: number;
|
|
162
|
+
limit: number;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export interface ListDocumentsResponse {
|
|
166
|
+
items: DocumentSummary[];
|
|
167
|
+
total: number;
|
|
168
|
+
page: number;
|
|
169
|
+
limit: number;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// --- Client ----------------------------------------------------------------
|
|
173
|
+
|
|
174
|
+
export const client = {
|
|
175
|
+
search(params: {
|
|
176
|
+
query: string;
|
|
177
|
+
folder?: string;
|
|
178
|
+
tags?: string[];
|
|
179
|
+
limit?: number;
|
|
180
|
+
}): Promise<SearchResponse> {
|
|
181
|
+
return request("GET", "/api/search", {
|
|
182
|
+
query: {
|
|
183
|
+
q: params.query,
|
|
184
|
+
folder: params.folder,
|
|
185
|
+
tags: params.tags,
|
|
186
|
+
limit: params.limit,
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
listDocuments(params: {
|
|
192
|
+
folderId?: string;
|
|
193
|
+
tag?: string;
|
|
194
|
+
page?: number;
|
|
195
|
+
limit?: number;
|
|
196
|
+
}): Promise<ListDocumentsResponse> {
|
|
197
|
+
return request("GET", "/api/documents", { query: params });
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
getDocument(id: string): Promise<DocumentDetail> {
|
|
201
|
+
return request("GET", `/api/${joinId("documents", id)}`);
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
createDocument(input: {
|
|
205
|
+
title: string;
|
|
206
|
+
content?: string;
|
|
207
|
+
folderId?: string;
|
|
208
|
+
}): Promise<DocumentDetail> {
|
|
209
|
+
return request("POST", "/api/documents", { body: input });
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
updateDocument(
|
|
213
|
+
id: string,
|
|
214
|
+
input: { title?: string; content?: string; folderId?: string | null },
|
|
215
|
+
): Promise<DocumentDetail> {
|
|
216
|
+
return request("PATCH", `/api/${joinId("documents", id)}`, { body: input });
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
deleteDocument(id: string): Promise<{ success: boolean }> {
|
|
220
|
+
return request("DELETE", `/api/${joinId("documents", id)}`);
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
exportDocument(id: string): Promise<string> {
|
|
224
|
+
return request("GET", `/api/${joinId("documents", id, "export")}`, {
|
|
225
|
+
accept: "text/markdown",
|
|
226
|
+
});
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
createSnapshot(
|
|
230
|
+
documentId: string,
|
|
231
|
+
input: { label: string; description?: string },
|
|
232
|
+
): Promise<SnapshotRow> {
|
|
233
|
+
// The backend exposes snapshots via POST /api/documents/:id/versions
|
|
234
|
+
// with isSnapshot=true (see backend/src/api/routes/versions.ts).
|
|
235
|
+
return request(
|
|
236
|
+
"POST",
|
|
237
|
+
`/api/${joinId("documents", documentId, "versions")}`,
|
|
238
|
+
{ body: input },
|
|
239
|
+
);
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
listVersions(
|
|
243
|
+
documentId: string,
|
|
244
|
+
onlySnapshots?: boolean,
|
|
245
|
+
): Promise<VersionRow[]> {
|
|
246
|
+
return request(
|
|
247
|
+
"GET",
|
|
248
|
+
`/api/${joinId("documents", documentId, "versions")}`,
|
|
249
|
+
{ query: { onlySnapshots } },
|
|
250
|
+
);
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
restoreVersion(documentId: string, versionId: string): Promise<DocumentDetail> {
|
|
254
|
+
return request(
|
|
255
|
+
"POST",
|
|
256
|
+
`/api/${joinId("documents", documentId, "versions", versionId, "restore")}`,
|
|
257
|
+
);
|
|
258
|
+
},
|
|
259
|
+
|
|
260
|
+
listFolders(params: { parentId?: string }): Promise<Folder[]> {
|
|
261
|
+
return request("GET", "/api/folders", { query: { parentId: params.parentId } });
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
createFolder(input: { name: string; parentId?: string }): Promise<Folder> {
|
|
265
|
+
return request("POST", "/api/folders", { body: input });
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
export type HiaiDocsClient = typeof client;
|
|
270
|
+
|
|
271
|
+
export type { Config };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `hiai-docs config` — save or update the CLI config file.
|
|
3
|
+
*
|
|
4
|
+
* Both --url and --key are optional: with neither supplied the
|
|
5
|
+
* command just prints the current configuration. With either
|
|
6
|
+
* supplied, only the provided keys are updated (the other is
|
|
7
|
+
* preserved from the existing file or env var).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { Command } from "commander";
|
|
11
|
+
import { loadConfig, saveConfig } from "../config.js";
|
|
12
|
+
import { dim, formatError, green } from "../format.js";
|
|
13
|
+
|
|
14
|
+
export function registerConfig(program: Command) {
|
|
15
|
+
program
|
|
16
|
+
.command("config")
|
|
17
|
+
.description("View or update CLI configuration")
|
|
18
|
+
.option("--url <url>", "API base URL")
|
|
19
|
+
.option("--key <key>", "API key (Bearer token)")
|
|
20
|
+
.option("--show", "Print the resolved configuration and exit")
|
|
21
|
+
.action(
|
|
22
|
+
async (opts: { url?: string; key?: string; show?: boolean }) => {
|
|
23
|
+
try {
|
|
24
|
+
const current = loadConfig();
|
|
25
|
+
if (opts.show || (opts.url === undefined && opts.key === undefined)) {
|
|
26
|
+
process.stdout.write(`url: ${current.url}\n`);
|
|
27
|
+
process.stdout.write(
|
|
28
|
+
`key: ${current.apiKey ? `${current.apiKey.slice(0, 4)}…(redacted)` : "(unset)"}\n`,
|
|
29
|
+
);
|
|
30
|
+
process.stdout.write(
|
|
31
|
+
`${dim("Env vars HIAI_DOCS_URL / HIAI_DOCS_API_KEY override the file.")}\n`,
|
|
32
|
+
);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const next = {
|
|
36
|
+
url: opts.url ?? current.url,
|
|
37
|
+
apiKey: opts.key ?? current.apiKey,
|
|
38
|
+
};
|
|
39
|
+
saveConfig(next);
|
|
40
|
+
process.stdout.write(`${green("✓")} Configuration saved.\n`);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
process.stderr.write(`${formatError(err)}\n`);
|
|
43
|
+
process.exitCode = 1;
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `hiai-docs create` — create a new document.
|
|
3
|
+
*
|
|
4
|
+
* Optional `--content` lets you inline markdown from the CLI; if you
|
|
5
|
+
* pipe a file (`cat spec.md | hiai-docs create --title "Spec"` —
|
|
6
|
+
* reserved for a future stdin flag) you'd extend this command.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Command } from "commander";
|
|
10
|
+
import { client, type HiaiDocsClient } from "../client.js";
|
|
11
|
+
import { formatError } from "../format.js";
|
|
12
|
+
|
|
13
|
+
export function registerCreate(program: Command, _getClient: () => HiaiDocsClient) {
|
|
14
|
+
program
|
|
15
|
+
.command("create")
|
|
16
|
+
.description("Create a new document")
|
|
17
|
+
.requiredOption("--title <title>", "Document title")
|
|
18
|
+
.option("-c, --content <markdown>", "Initial markdown content")
|
|
19
|
+
.option("-f, --folder <uuid>", "Place in folder")
|
|
20
|
+
.action(
|
|
21
|
+
async (opts: { title: string; content?: string; folder?: string }) => {
|
|
22
|
+
try {
|
|
23
|
+
const doc = await client.createDocument({
|
|
24
|
+
title: opts.title,
|
|
25
|
+
content: opts.content,
|
|
26
|
+
folderId: opts.folder,
|
|
27
|
+
});
|
|
28
|
+
process.stdout.write(`${doc.id}\n`);
|
|
29
|
+
} catch (err) {
|
|
30
|
+
process.stderr.write(`${formatError(err)}\n`);
|
|
31
|
+
process.exitCode = 1;
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
}
|