@hasna/knowledge 0.2.2 → 0.2.4
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 +86 -4
- package/bin/open-knowledge-mcp.js +14452 -0
- package/bin/open-knowledge.js +227 -4
- package/docs/architecture/ai-native-knowledge-base.md +191 -0
- package/docs/architecture/hybrid-semantic-search.md +135 -0
- package/package.json +20 -4
- package/src/artifact-store.ts +184 -0
- package/src/cli.ts +181 -25
- package/src/knowledge-db.ts +231 -0
- package/src/mcp.js +374 -415
- package/src/source-ref.ts +92 -0
- package/src/store.ts +16 -6
- package/src/wiki-layout.ts +104 -0
- package/src/workspace.ts +123 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -59
- package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -34
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -21
- package/.github/workflows/ci.yml +0 -49
- package/.takumi/settings.local.json +0 -7
- package/CODE_OF_CONDUCT.md +0 -31
- package/CONTRIBUTING.md +0 -83
- package/FUNDING.yml +0 -1
- package/SECURITY.md +0 -39
- package/npmignore +0 -9
- package/tests/cli.test.ts +0 -91
- package/tests/mcp-http.test.ts +0 -97
- package/tsconfig.json +0 -16
package/SECURITY.md
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# Security Policy
|
|
2
|
-
|
|
3
|
-
## Supported Versions
|
|
4
|
-
|
|
5
|
-
| Version | Supported |
|
|
6
|
-
| ------- | ------------------ |
|
|
7
|
-
| 0.1.x | :white_check_mark: |
|
|
8
|
-
|
|
9
|
-
## Reporting a Vulnerability
|
|
10
|
-
|
|
11
|
-
If you discover a security vulnerability, please report it responsibly.
|
|
12
|
-
|
|
13
|
-
**Do not open a public GitHub issue** for security vulnerabilities.
|
|
14
|
-
|
|
15
|
-
Please send details privately:
|
|
16
|
-
|
|
17
|
-
1. **Email**: Send to the maintainer directly via GitHub.
|
|
18
|
-
2. **GitHub Security Advisories**: Use the [Security Advisories](https://github.com/hasna/knowledge/security/advisories/new) feature to report privately.
|
|
19
|
-
|
|
20
|
-
Include in your report:
|
|
21
|
-
- Description of the vulnerability
|
|
22
|
-
- Steps to reproduce
|
|
23
|
-
- Potential impact
|
|
24
|
-
- Any suggested fixes (optional)
|
|
25
|
-
|
|
26
|
-
## Response Timeline
|
|
27
|
-
|
|
28
|
-
- **Acknowledgment**: within 48 hours
|
|
29
|
-
- **Initial assessment**: within 5 days
|
|
30
|
-
- **Fix timeline**: depends on severity; critical issues are addressed immediately
|
|
31
|
-
|
|
32
|
-
## Scope
|
|
33
|
-
|
|
34
|
-
This project stores data in a local JSON file (`~/.open-knowledge/db.json` by default). Security considerations:
|
|
35
|
-
|
|
36
|
-
- Store file permissions should be restricted to the owner
|
|
37
|
-
- No network access or remote code execution
|
|
38
|
-
- No authentication (local CLI tool)
|
|
39
|
-
- Encryption of the store file is not currently implemented
|
package/npmignore
DELETED
package/tests/cli.test.ts
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @hasna/knowledge
|
|
3
|
-
* Copyright 2026 Hasna Inc.
|
|
4
|
-
* Licensed under the Apache License, Version 2.0
|
|
5
|
-
*/
|
|
6
|
-
import { describe, expect, test } from 'bun:test';
|
|
7
|
-
import { mkdtempSync, readFileSync } from 'node:fs';
|
|
8
|
-
import { tmpdir } from 'node:os';
|
|
9
|
-
import { join, dirname } from 'node:path';
|
|
10
|
-
import { fileURLToPath } from 'node:url';
|
|
11
|
-
|
|
12
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
-
const CLI = join(__dirname, '..', 'src', 'cli.ts');
|
|
14
|
-
|
|
15
|
-
function runCli(args: string[]) {
|
|
16
|
-
return Bun.spawnSync(['bun', CLI, ...args], {
|
|
17
|
-
stdout: 'pipe',
|
|
18
|
-
stderr: 'pipe'
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
describe('open-knowledge cli', () => {
|
|
23
|
-
test('help and subcommand help work', () => {
|
|
24
|
-
const result = runCli(['--help']);
|
|
25
|
-
expect(result.exitCode).toBe(0);
|
|
26
|
-
const out = new TextDecoder().decode(result.stdout);
|
|
27
|
-
expect(out).toContain('open-knowledge');
|
|
28
|
-
expect(out).toContain('Commands:');
|
|
29
|
-
|
|
30
|
-
const sub = runCli(['help', 'list']);
|
|
31
|
-
expect(sub.exitCode).toBe(0);
|
|
32
|
-
const subOut = new TextDecoder().decode(sub.stdout);
|
|
33
|
-
expect(subOut).toContain('--sort created|title');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test('version flag works', () => {
|
|
37
|
-
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8')) as { version: string };
|
|
38
|
-
const result = runCli(['--version']);
|
|
39
|
-
expect(result.exitCode).toBe(0);
|
|
40
|
-
const out = new TextDecoder().decode(result.stdout);
|
|
41
|
-
expect(out.trim()).toBe(pkg.version);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test('unknown command includes suggestion', () => {
|
|
45
|
-
const result = runCli(['lits']);
|
|
46
|
-
expect(result.exitCode).toBe(1);
|
|
47
|
-
const err = new TextDecoder().decode(result.stderr);
|
|
48
|
-
expect(err).toContain("Did you mean 'list'");
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test('add/list/get/delete flow with json and confirmation', () => {
|
|
52
|
-
const dir = mkdtempSync(join(tmpdir(), 'ok-cli-'));
|
|
53
|
-
const store = join(dir, 'db.json');
|
|
54
|
-
|
|
55
|
-
const addA = runCli(['add', 'TitleB', 'BodyA', '--store', store, '--json']);
|
|
56
|
-
expect(addA.exitCode).toBe(0);
|
|
57
|
-
const addAOut = JSON.parse(new TextDecoder().decode(addA.stdout));
|
|
58
|
-
|
|
59
|
-
const addB = runCli(['add', 'TitleA', 'BodyB', '--store', store, '--json']);
|
|
60
|
-
expect(addB.exitCode).toBe(0);
|
|
61
|
-
const addBOut = JSON.parse(new TextDecoder().decode(addB.stdout));
|
|
62
|
-
|
|
63
|
-
const list = runCli(['ls', '--store', store, '--json', '-p', '1', '-l', '10', '--sort', 'title']);
|
|
64
|
-
expect(list.exitCode).toBe(0);
|
|
65
|
-
const listOut = JSON.parse(new TextDecoder().decode(list.stdout));
|
|
66
|
-
expect(listOut.total).toBe(2);
|
|
67
|
-
expect(listOut.total_pages).toBe(1);
|
|
68
|
-
expect(listOut.items[0].title).toBe('TitleA');
|
|
69
|
-
|
|
70
|
-
const get = runCli(['get', '--id', addAOut.item.id, '--store', store, '--json']);
|
|
71
|
-
expect(get.exitCode).toBe(0);
|
|
72
|
-
const getOut = JSON.parse(new TextDecoder().decode(get.stdout));
|
|
73
|
-
expect(getOut.item.content).toBe('BodyA');
|
|
74
|
-
|
|
75
|
-
const delNoYes = runCli(['rm', '--id', addAOut.item.id, '--store', store, '--json']);
|
|
76
|
-
expect(delNoYes.exitCode).toBe(1);
|
|
77
|
-
const delErr = new TextDecoder().decode(delNoYes.stderr);
|
|
78
|
-
expect(delErr).toContain('Refusing delete without --yes');
|
|
79
|
-
|
|
80
|
-
const del = runCli(['delete', '--id', addAOut.item.id, '--store', store, '--json', '--yes']);
|
|
81
|
-
expect(del.exitCode).toBe(0);
|
|
82
|
-
const delOut = JSON.parse(new TextDecoder().decode(del.stdout));
|
|
83
|
-
expect(delOut.ok).toBe(true);
|
|
84
|
-
|
|
85
|
-
const del2 = runCli(['delete', '--id', addBOut.item.id, '--store', store, '--json', '--yes']);
|
|
86
|
-
expect(del2.exitCode).toBe(0);
|
|
87
|
-
|
|
88
|
-
const db = JSON.parse(readFileSync(store, 'utf8'));
|
|
89
|
-
expect(db.items.length).toBe(0);
|
|
90
|
-
});
|
|
91
|
-
});
|
package/tests/mcp-http.test.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
|
|
2
|
-
import { mkdtempSync } from 'node:fs';
|
|
3
|
-
import { tmpdir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
6
|
-
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
7
|
-
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
|
|
8
|
-
import { buildServer } from '../src/mcp.js';
|
|
9
|
-
import {
|
|
10
|
-
DEFAULT_MCP_HTTP_PORT,
|
|
11
|
-
isHttpMode,
|
|
12
|
-
resolveMcpHttpPort,
|
|
13
|
-
startMcpHttpServer,
|
|
14
|
-
} from '../src/mcp-http.js';
|
|
15
|
-
|
|
16
|
-
const storePath = join(mkdtempSync(join(tmpdir(), 'knowledge-mcp-http-')), 'db.json');
|
|
17
|
-
|
|
18
|
-
describe('knowledge MCP HTTP transport', () => {
|
|
19
|
-
test('defaults port to 8819', () => {
|
|
20
|
-
expect(DEFAULT_MCP_HTTP_PORT).toBe(8819);
|
|
21
|
-
expect(resolveMcpHttpPort(['node'], {})).toBe(8819);
|
|
22
|
-
expect(resolveMcpHttpPort(['node', '--port', '9001'], {})).toBe(9001);
|
|
23
|
-
expect(resolveMcpHttpPort(['node'], { MCP_HTTP_PORT: '9002' })).toBe(9002);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test('isHttpMode detects flag and env', () => {
|
|
27
|
-
expect(isHttpMode(['node'], {})).toBe(false);
|
|
28
|
-
expect(isHttpMode(['node', '--http'], {})).toBe(true);
|
|
29
|
-
expect(isHttpMode(['node'], { MCP_HTTP: '1' })).toBe(true);
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
describe('knowledge buildServer stdio registration', () => {
|
|
34
|
-
test('registers tools over in-memory transport', async () => {
|
|
35
|
-
const server = buildServer();
|
|
36
|
-
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
|
|
37
|
-
await server.connect(serverTransport);
|
|
38
|
-
|
|
39
|
-
const client = new Client({ name: 'test', version: '0.0.0' });
|
|
40
|
-
await client.connect(clientTransport);
|
|
41
|
-
|
|
42
|
-
const tools = await client.listTools();
|
|
43
|
-
expect(tools.tools.some((tool) => tool.name === 'ok_stats')).toBe(true);
|
|
44
|
-
|
|
45
|
-
await client.close();
|
|
46
|
-
await server.close();
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
describe('knowledge streamable HTTP server', () => {
|
|
51
|
-
let handle: Awaited<ReturnType<typeof startMcpHttpServer>>;
|
|
52
|
-
|
|
53
|
-
beforeAll(async () => {
|
|
54
|
-
handle = await startMcpHttpServer(buildServer, { port: 0 });
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
afterAll(async () => {
|
|
58
|
-
await handle.close();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test('GET /health returns ok', async () => {
|
|
62
|
-
const res = await fetch(`http://${handle.host}:${handle.port}/health`);
|
|
63
|
-
expect(res.status).toBe(200);
|
|
64
|
-
expect(await res.json()).toEqual({ status: 'ok', name: 'knowledge' });
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test('initialize and call ok_stats over streamable HTTP', async () => {
|
|
68
|
-
const transport = new StreamableHTTPClientTransport(
|
|
69
|
-
new URL(`http://${handle.host}:${handle.port}/mcp`),
|
|
70
|
-
);
|
|
71
|
-
const client = new Client({ name: 'test', version: '0.0.0' });
|
|
72
|
-
await client.connect(transport);
|
|
73
|
-
|
|
74
|
-
const result = await client.callTool({ name: 'ok_stats', arguments: { store_path: storePath } });
|
|
75
|
-
expect(result.content).toBeDefined();
|
|
76
|
-
expect(Array.isArray(result.content)).toBe(true);
|
|
77
|
-
|
|
78
|
-
await client.close();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test('serves three concurrent clients from one process', async () => {
|
|
82
|
-
const clients = await Promise.all(
|
|
83
|
-
Array.from({ length: 3 }, async () => {
|
|
84
|
-
const transport = new StreamableHTTPClientTransport(
|
|
85
|
-
new URL(`http://${handle.host}:${handle.port}/mcp`),
|
|
86
|
-
);
|
|
87
|
-
const client = new Client({ name: 'test', version: '0.0.0' });
|
|
88
|
-
await client.connect(transport);
|
|
89
|
-
const tools = await client.listTools();
|
|
90
|
-
return { client, count: tools.tools.length };
|
|
91
|
-
}),
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
expect(clients.every((entry) => entry.count > 0)).toBe(true);
|
|
95
|
-
await Promise.all(clients.map((entry) => entry.client.close()));
|
|
96
|
-
});
|
|
97
|
-
});
|
package/tsconfig.json
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ES2022",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"lib": ["ES2022"],
|
|
7
|
-
"outDir": "./dist",
|
|
8
|
-
"rootDir": "./src",
|
|
9
|
-
"strict": false,
|
|
10
|
-
"skipLibCheck": true,
|
|
11
|
-
"allowImportingExtensions": true,
|
|
12
|
-
"noEmit": true
|
|
13
|
-
},
|
|
14
|
-
"include": ["src/**/*", "tests/**/*"],
|
|
15
|
-
"exclude": ["node_modules", "dist", "bin"]
|
|
16
|
-
}
|