@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/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
@@ -1,9 +0,0 @@
1
- tests/
2
- .github/
3
- .gitignore
4
- .git/
5
- .env*
6
- *.test.ts
7
- *.test.js
8
- tsconfig.json
9
- bun.lockb
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
- });
@@ -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
- }