@hasna/knowledge 0.2.9 → 0.2.10

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/src/cli.ts CHANGED
@@ -5,15 +5,9 @@
5
5
  * Licensed under the Apache License, Version 2.0
6
6
  */
7
7
  import { defaultStorePath, loadStore, saveStore, withLock, makeId, makeShortId, ensureStore, type KnowledgeItem } from './store';
8
- import { ensureKnowledgeWorkspace, readKnowledgeConfig, resolveScopedWorkspace } from './workspace';
9
- import { getKnowledgeDbStats, migrateKnowledgeDb, openKnowledgeDb } from './knowledge-db';
10
- import { createArtifactStore } from './artifact-store';
11
- import { initializeWikiLayout } from './wiki-layout';
12
- import { ingestOpenFilesManifest } from './manifest-ingest';
13
- import { ingestSourceRef } from './source-ingest';
14
- import { consumeOpenFilesOutbox } from './outbox-consume';
15
- import { resolveOpenFilesSource } from './source-resolver';
16
- import { approvalStatus, assertS3ReadAllowed, assertWebSearchAllowed, createApprovalGate, recordAuditEvent, recordRedactionFindings, redactSecrets, resolveSafetyPolicy } from './safety';
8
+ import { openKnowledgeDb } from './knowledge-db';
9
+ import { createKnowledgeService } from './service';
10
+ import { approvalStatus, assertS3ReadAllowed, assertWebSearchAllowed, createApprovalGate, recordAuditEvent, recordRedactionFindings, redactSecrets } from './safety';
17
11
  import pkg from '../package.json' with { type: 'json' };
18
12
 
19
13
  type LogLevel = 'debug' | 'info' | 'warn' | 'error';
@@ -299,71 +293,49 @@ async function run(argv: string[]): Promise<void> {
299
293
 
300
294
  if (!command || flags.help || command === 'help') { printCommandHelp(positional[1]); return; }
301
295
 
302
- const workspace = resolveScopedWorkspace(flags.scope);
296
+ const service = createKnowledgeService({ scope: flags.scope });
303
297
  let storePath = flags.store;
304
298
  if (!storePath) {
305
299
  if (flags.scope === 'project' || flags.scope === 'local') {
306
- storePath = ensureKnowledgeWorkspace(workspace.home).jsonStorePath;
300
+ storePath = service.jsonStorePath();
307
301
  } else {
308
302
  storePath = defaultStorePath();
309
303
  }
310
304
  }
311
305
 
312
306
  if (command === 'paths') {
313
- const resolvedWorkspace = ensureKnowledgeWorkspace(workspace.home);
314
- output({
315
- ok: true,
316
- scope: flags.scope ?? 'global',
317
- home: resolvedWorkspace.home,
318
- config_path: resolvedWorkspace.configPath,
319
- json_store_path: resolvedWorkspace.jsonStorePath,
320
- knowledge_db_path: resolvedWorkspace.knowledgeDbPath,
321
- artifacts_dir: resolvedWorkspace.artifactsDir,
322
- indexes_dir: resolvedWorkspace.indexesDir,
323
- logs_dir: resolvedWorkspace.logsDir,
324
- runs_dir: resolvedWorkspace.runsDir,
325
- schemas_dir: resolvedWorkspace.schemasDir,
326
- wiki_dir: resolvedWorkspace.wikiDir,
327
- config: readKnowledgeConfig(resolvedWorkspace.configPath),
328
- message: resolvedWorkspace.home,
329
- }, flags.json);
307
+ output(service.paths(), flags.json);
330
308
  return;
331
309
  }
332
310
 
333
311
  if (command === 'db') {
334
312
  const action = positional[1] ?? 'init';
335
- const resolvedWorkspace = ensureKnowledgeWorkspace(workspace.home);
336
313
  if (action !== 'init' && action !== 'stats') {
337
314
  throw new Error("Invalid db action. Use 'init' or 'stats'.");
338
315
  }
339
316
  if (action === 'init') {
340
- const result = migrateKnowledgeDb(resolvedWorkspace.knowledgeDbPath);
317
+ const result = service.initDb();
341
318
  output({ ok: true, ...result, message: `Initialized ${result.path}` }, flags.json);
342
319
  return;
343
320
  }
344
- migrateKnowledgeDb(resolvedWorkspace.knowledgeDbPath);
345
- const stats = getKnowledgeDbStats(resolvedWorkspace.knowledgeDbPath);
346
- output({ ok: true, path: resolvedWorkspace.knowledgeDbPath, ...stats, message: `knowledge.db schema v${stats.schema_version}` }, flags.json);
321
+ const stats = service.dbStats();
322
+ output({ ok: true, path: service.workspace.knowledgeDbPath, ...stats, message: `knowledge.db schema v${stats.schema_version}` }, flags.json);
347
323
  return;
348
324
  }
349
325
 
350
326
  if (command === 'wiki') {
351
327
  const action = positional[1] ?? 'init';
352
328
  if (action !== 'init') throw new Error("Invalid wiki action. Use 'init'.");
353
- const resolvedWorkspace = ensureKnowledgeWorkspace(workspace.home);
354
- const config = readKnowledgeConfig(resolvedWorkspace.configPath);
355
- const artifactStore = createArtifactStore(config, resolvedWorkspace);
356
- const result = await initializeWikiLayout(artifactStore);
357
- output({ ok: true, ...result, message: `Initialized wiki layout in ${resolvedWorkspace.home}` }, flags.json);
329
+ const result = await service.initWiki();
330
+ output({ ok: true, ...result, message: `Initialized wiki layout in ${service.workspace.home}` }, flags.json);
358
331
  return;
359
332
  }
360
333
 
361
334
  if (command === 'safety') {
362
335
  const action = positional[1] ?? 'status';
363
- const resolvedWorkspace = ensureKnowledgeWorkspace(workspace.home);
364
- const config = readKnowledgeConfig(resolvedWorkspace.configPath);
365
- const policy = resolveSafetyPolicy(config, resolvedWorkspace);
366
- migrateKnowledgeDb(resolvedWorkspace.knowledgeDbPath);
336
+ const resolvedWorkspace = service.ensureWorkspace();
337
+ const policy = service.safetyPolicy();
338
+ service.initDb();
367
339
  const db = openKnowledgeDb(resolvedWorkspace.knowledgeDbPath);
368
340
  try {
369
341
  if (action === 'status') {
@@ -489,15 +461,9 @@ async function run(argv: string[]): Promise<void> {
489
461
  if (action !== 'resolve') throw new Error("Invalid source action. Use 'resolve'.");
490
462
  const sourceRef = positional[2];
491
463
  if (!sourceRef) throw new Error('Usage: open-knowledge source resolve <source-ref>');
492
- const resolvedWorkspace = ensureKnowledgeWorkspace(workspace.home);
493
- const config = readKnowledgeConfig(resolvedWorkspace.configPath);
494
- const safetyPolicy = resolveSafetyPolicy(config, resolvedWorkspace);
495
- const result = await resolveOpenFilesSource({
496
- dbPath: resolvedWorkspace.knowledgeDbPath,
497
- sourceRef,
464
+ const result = await service.resolveSource(sourceRef, {
498
465
  purpose: flags.purpose,
499
466
  limit: flags.limit,
500
- safetyPolicy,
501
467
  });
502
468
  output({
503
469
  ok: true,
@@ -511,31 +477,17 @@ async function run(argv: string[]): Promise<void> {
511
477
 
512
478
  if (command === 'ingest') {
513
479
  const action = positional[1] ?? '';
514
- const resolvedWorkspace = ensureKnowledgeWorkspace(workspace.home);
515
- const config = readKnowledgeConfig(resolvedWorkspace.configPath);
516
- const safetyPolicy = resolveSafetyPolicy(config, resolvedWorkspace);
517
480
  if (action === 'manifest') {
518
481
  const input = positional[2];
519
482
  if (!input) throw new Error('Usage: open-knowledge ingest manifest <file|s3://bucket/key>');
520
- const result = await ingestOpenFilesManifest({
521
- dbPath: resolvedWorkspace.knowledgeDbPath,
522
- input,
523
- config,
524
- safetyPolicy,
525
- });
483
+ const result = await service.ingestManifest(input);
526
484
  output({ ok: true, ...result, message: `Ingested ${result.items_seen} manifest item(s)` }, flags.json);
527
485
  return;
528
486
  }
529
487
  if (action === 'source') {
530
488
  const sourceRef = positional[2];
531
489
  if (!sourceRef) throw new Error('Usage: open-knowledge ingest source <source-ref>');
532
- const result = await ingestSourceRef({
533
- dbPath: resolvedWorkspace.knowledgeDbPath,
534
- sourceRef,
535
- purpose: flags.purpose,
536
- config,
537
- safetyPolicy,
538
- });
490
+ const result = await service.ingestSource(sourceRef, flags.purpose);
539
491
  output({ ok: true, ...result, message: `Ingested source ${result.source_ref} (${result.chunks_inserted} chunks)` }, flags.json);
540
492
  return;
541
493
  }
@@ -547,15 +499,7 @@ async function run(argv: string[]): Promise<void> {
547
499
  if (action !== 'outbox') throw new Error("Invalid reindex action. Use 'outbox'.");
548
500
  const input = positional[2];
549
501
  if (!input) throw new Error('Usage: open-knowledge reindex outbox <file|s3://bucket/key>');
550
- const resolvedWorkspace = ensureKnowledgeWorkspace(workspace.home);
551
- const config = readKnowledgeConfig(resolvedWorkspace.configPath);
552
- const safetyPolicy = resolveSafetyPolicy(config, resolvedWorkspace);
553
- const result = await consumeOpenFilesOutbox({
554
- dbPath: resolvedWorkspace.knowledgeDbPath,
555
- input,
556
- config,
557
- safetyPolicy,
558
- });
502
+ const result = await service.consumeOutbox(input);
559
503
  output({ ok: true, ...result, message: `Consumed ${result.events_seen} outbox event(s)` }, flags.json);
560
504
  return;
561
505
  }
package/src/mcp.js CHANGED
@@ -5,10 +5,8 @@ import { z } from 'zod';
5
5
  import { existsSync, readFileSync, writeFileSync } from 'node:fs';
6
6
  import pkg from '../package.json' with { type: 'json' };
7
7
  import { defaultStorePath, loadStore, saveStore, makeId, withLock } from './store.ts';
8
- import { ensureKnowledgeWorkspace, readKnowledgeConfig, resolveScopedWorkspace } from './workspace.ts';
9
8
  import { parseSourceRef } from './source-ref.ts';
10
- import { resolveOpenFilesSource } from './source-resolver.ts';
11
- import { resolveSafetyPolicy } from './safety.ts';
9
+ import { createKnowledgeService } from './service.ts';
12
10
 
13
11
  const storePathField = z.string().optional().describe('Path to the JSON store file');
14
12
  const scopeField = z.enum(['local', 'global', 'project']).optional().describe('Workspace scope');
@@ -28,7 +26,7 @@ function shortIdFor(id) {
28
26
  function resolveStorePath(storePath, scope) {
29
27
  if (storePath) return storePath;
30
28
  if (scope === 'project' || scope === 'local') {
31
- return ensureKnowledgeWorkspace(resolveScopedWorkspace(scope).home).jsonStorePath;
29
+ return createKnowledgeService({ scope }).jsonStorePath();
32
30
  }
33
31
  return defaultStorePath();
34
32
  }
@@ -76,22 +74,7 @@ export function buildServer() {
76
74
  registerTool(server, 'ok_paths', 'Knowledge workspace paths', 'Show resolved workspace and store paths', {
77
75
  scope: scopeField,
78
76
  }, async ({ scope }) => {
79
- const workspace = ensureKnowledgeWorkspace(resolveScopedWorkspace(scope).home);
80
- return jsonText({
81
- ok: true,
82
- scope: scope ?? 'global',
83
- home: workspace.home,
84
- config_path: workspace.configPath,
85
- json_store_path: workspace.jsonStorePath,
86
- knowledge_db_path: workspace.knowledgeDbPath,
87
- artifacts_dir: workspace.artifactsDir,
88
- indexes_dir: workspace.indexesDir,
89
- logs_dir: workspace.logsDir,
90
- runs_dir: workspace.runsDir,
91
- schemas_dir: workspace.schemasDir,
92
- wiki_dir: workspace.wikiDir,
93
- config: readKnowledgeConfig(workspace.configPath),
94
- });
77
+ return jsonText(createKnowledgeService({ scope }).paths());
95
78
  });
96
79
 
97
80
  registerTool(server, 'ok_parse_source_ref', 'Parse source reference', 'Parse and validate an open-files, S3, file, or web source ref', {
@@ -110,16 +93,11 @@ export function buildServer() {
110
93
  limit: z.number().optional().describe('Maximum chunks to return, default 10'),
111
94
  scope: scopeField,
112
95
  }, async ({ source_ref, purpose, limit, scope }) => {
113
- const workspace = ensureKnowledgeWorkspace(resolveScopedWorkspace(scope).home);
114
- const config = readKnowledgeConfig(workspace.configPath);
115
- const safetyPolicy = resolveSafetyPolicy(config, workspace);
96
+ const service = createKnowledgeService({ scope });
116
97
  try {
117
- const result = await resolveOpenFilesSource({
118
- dbPath: workspace.knowledgeDbPath,
119
- sourceRef: source_ref,
98
+ const result = await service.resolveSource(source_ref, {
120
99
  purpose,
121
100
  limit,
122
- safetyPolicy,
123
101
  });
124
102
  return jsonText({ ok: true, ...result });
125
103
  } catch (error) {
package/src/service.ts ADDED
@@ -0,0 +1,157 @@
1
+ import { createArtifactStore } from './artifact-store';
2
+ import { consumeOpenFilesOutbox } from './outbox-consume';
3
+ import { getKnowledgeDbStats, migrateKnowledgeDb } from './knowledge-db';
4
+ import { ingestOpenFilesManifest } from './manifest-ingest';
5
+ import { ingestSourceRef } from './source-ingest';
6
+ import { resolveOpenFilesSource } from './source-resolver';
7
+ import { resolveSafetyPolicy } from './safety';
8
+ import { initializeWikiLayout } from './wiki-layout';
9
+ import {
10
+ ensureKnowledgeWorkspace,
11
+ readKnowledgeConfig,
12
+ resolveScopedWorkspace,
13
+ type KnowledgeConfig,
14
+ type KnowledgeWorkspace,
15
+ } from './workspace';
16
+
17
+ export interface KnowledgeServiceOptions {
18
+ scope?: string;
19
+ cwd?: string;
20
+ }
21
+
22
+ export interface KnowledgePathsResult {
23
+ ok: true;
24
+ scope: string;
25
+ home: string;
26
+ config_path: string;
27
+ json_store_path: string;
28
+ knowledge_db_path: string;
29
+ artifacts_dir: string;
30
+ indexes_dir: string;
31
+ logs_dir: string;
32
+ runs_dir: string;
33
+ schemas_dir: string;
34
+ wiki_dir: string;
35
+ config: KnowledgeConfig;
36
+ message: string;
37
+ }
38
+
39
+ export class KnowledgeService {
40
+ private ensuredWorkspace?: KnowledgeWorkspace;
41
+ private cachedConfig?: KnowledgeConfig;
42
+
43
+ constructor(private readonly options: KnowledgeServiceOptions = {}) {}
44
+
45
+ get scope(): string {
46
+ return this.options.scope ?? 'global';
47
+ }
48
+
49
+ get workspace(): KnowledgeWorkspace {
50
+ return this.ensuredWorkspace ?? resolveScopedWorkspace(this.options.scope, this.options.cwd);
51
+ }
52
+
53
+ ensureWorkspace(): KnowledgeWorkspace {
54
+ if (!this.ensuredWorkspace) this.ensuredWorkspace = ensureKnowledgeWorkspace(this.workspace.home);
55
+ return this.ensuredWorkspace;
56
+ }
57
+
58
+ jsonStorePath(): string {
59
+ return this.ensureWorkspace().jsonStorePath;
60
+ }
61
+
62
+ config(): KnowledgeConfig {
63
+ if (!this.cachedConfig) {
64
+ const workspace = this.ensureWorkspace();
65
+ this.cachedConfig = readKnowledgeConfig(workspace.configPath);
66
+ }
67
+ return this.cachedConfig;
68
+ }
69
+
70
+ safetyPolicy() {
71
+ return resolveSafetyPolicy(this.config(), this.ensureWorkspace());
72
+ }
73
+
74
+ artifactStore() {
75
+ return createArtifactStore(this.config(), this.ensureWorkspace());
76
+ }
77
+
78
+ paths(): KnowledgePathsResult {
79
+ const workspace = this.ensureWorkspace();
80
+ return {
81
+ ok: true,
82
+ scope: this.scope,
83
+ home: workspace.home,
84
+ config_path: workspace.configPath,
85
+ json_store_path: workspace.jsonStorePath,
86
+ knowledge_db_path: workspace.knowledgeDbPath,
87
+ artifacts_dir: workspace.artifactsDir,
88
+ indexes_dir: workspace.indexesDir,
89
+ logs_dir: workspace.logsDir,
90
+ runs_dir: workspace.runsDir,
91
+ schemas_dir: workspace.schemasDir,
92
+ wiki_dir: workspace.wikiDir,
93
+ config: this.config(),
94
+ message: workspace.home,
95
+ };
96
+ }
97
+
98
+ initDb() {
99
+ return migrateKnowledgeDb(this.ensureWorkspace().knowledgeDbPath);
100
+ }
101
+
102
+ dbStats() {
103
+ const workspace = this.ensureWorkspace();
104
+ migrateKnowledgeDb(workspace.knowledgeDbPath);
105
+ return getKnowledgeDbStats(workspace.knowledgeDbPath);
106
+ }
107
+
108
+ async initWiki() {
109
+ return initializeWikiLayout(this.artifactStore());
110
+ }
111
+
112
+ async ingestManifest(input: string) {
113
+ const workspace = this.ensureWorkspace();
114
+ return ingestOpenFilesManifest({
115
+ dbPath: workspace.knowledgeDbPath,
116
+ input,
117
+ config: this.config(),
118
+ safetyPolicy: this.safetyPolicy(),
119
+ });
120
+ }
121
+
122
+ async ingestSource(sourceRef: string, purpose?: string) {
123
+ const workspace = this.ensureWorkspace();
124
+ return ingestSourceRef({
125
+ dbPath: workspace.knowledgeDbPath,
126
+ sourceRef,
127
+ purpose,
128
+ config: this.config(),
129
+ safetyPolicy: this.safetyPolicy(),
130
+ });
131
+ }
132
+
133
+ async resolveSource(sourceRef: string, options: { purpose?: string; limit?: number } = {}) {
134
+ const workspace = this.ensureWorkspace();
135
+ return resolveOpenFilesSource({
136
+ dbPath: workspace.knowledgeDbPath,
137
+ sourceRef,
138
+ purpose: options.purpose,
139
+ limit: options.limit,
140
+ safetyPolicy: this.safetyPolicy(),
141
+ });
142
+ }
143
+
144
+ async consumeOutbox(input: string) {
145
+ const workspace = this.ensureWorkspace();
146
+ return consumeOpenFilesOutbox({
147
+ dbPath: workspace.knowledgeDbPath,
148
+ input,
149
+ config: this.config(),
150
+ safetyPolicy: this.safetyPolicy(),
151
+ });
152
+ }
153
+ }
154
+
155
+ export function createKnowledgeService(options: KnowledgeServiceOptions = {}): KnowledgeService {
156
+ return new KnowledgeService(options);
157
+ }