@hasna/knowledge 0.2.25 → 0.2.27
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 +35 -0
- package/bin/open-knowledge-mcp.js +71 -3
- package/bin/open-knowledge.js +86 -86
- package/docs/architecture/ai-native-knowledge-base.md +24 -0
- package/docs/architecture/hosted-wrapper-responsibilities.md +8 -0
- package/docs/canonical-secrets-bootstrap-2026-06-08.md +127 -0
- package/docs/examples/company-wiki-workflow.md +176 -0
- package/docs/migration/json-to-sqlite.md +151 -0
- package/package.json +1 -1
- package/src/cli.ts +8 -5
- package/src/service.ts +13 -2
- package/src/storage-contract.ts +54 -1
- package/src/workspace.ts +38 -0
|
@@ -159,6 +159,30 @@ s3://<knowledge-bucket>/<org>/<project>/knowledge/
|
|
|
159
159
|
wiki/
|
|
160
160
|
```
|
|
161
161
|
|
|
162
|
+
Hasna XYZ production uses the canonical open-source knowledge bucket and app
|
|
163
|
+
path-compatible prefix:
|
|
164
|
+
|
|
165
|
+
```text
|
|
166
|
+
s3://hasna-xyz-opensource-knowledge-prod/.hasna/apps/knowledge/
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
The app config can be materialized with:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
open-knowledge setup --mode hosted --canonical-hasna-xyz --scope project --json
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
The canonical metadata-only secret paths are:
|
|
176
|
+
|
|
177
|
+
```text
|
|
178
|
+
hasna/xyz/opensource/knowledge/prod/env
|
|
179
|
+
hasna/xyz/opensource/knowledge/prod/aws
|
|
180
|
+
hasna/xyz/opensource/knowledge/prod/s3
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`hasna/xyz/opensource/knowledge/prod/rds` is reserved for a future hosted
|
|
184
|
+
runtime database if the wrapper provisions one.
|
|
185
|
+
|
|
162
186
|
Raw files still route through `open-files`. Knowledge S3 storage is for derived
|
|
163
187
|
artifacts such as wiki pages, index shards, schema versions, logs, exports, and
|
|
164
188
|
run outputs.
|
|
@@ -86,6 +86,14 @@ The OSS package may know a storage contract, bucket name, prefix, region, and
|
|
|
86
86
|
profile. It must not contain tenant secret values, connector credentials, RDS
|
|
87
87
|
passwords, hosted KMS key material, or privileged AWS role assumptions.
|
|
88
88
|
|
|
89
|
+
For Hasna XYZ production, the OSS contract names
|
|
90
|
+
`hasna-xyz-opensource-knowledge-prod` and prefix
|
|
91
|
+
`.hasna/apps/knowledge/`, plus metadata-only secret paths under
|
|
92
|
+
`hasna/xyz/opensource/knowledge/prod/{env,aws,s3}`. Hosted code is responsible
|
|
93
|
+
for resolving those secrets, assuming AWS roles, enforcing tenant prefixes, and
|
|
94
|
+
provisioning `hasna/xyz/opensource/knowledge/prod/rds` only if a hosted runtime
|
|
95
|
+
database is introduced.
|
|
96
|
+
|
|
89
97
|
Generated artifacts are safe to sync remotely only when they remain derived:
|
|
90
98
|
wiki pages, index shards, schema files, logs, exports, run payloads, embeddings,
|
|
91
99
|
and citation metadata. Raw source bytes stay in `open-files`.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Canonical Secrets Bootstrap Evidence: 2026-06-08
|
|
2
|
+
|
|
3
|
+
Scope: canonical Hasna XYZ app secret paths in account `hasna-xyz-infra`
|
|
4
|
+
(`789877399345`), region `us-east-1`, mirrored into the local `spark02`
|
|
5
|
+
`secrets` vault.
|
|
6
|
+
|
|
7
|
+
This note records names and migration intent only. It intentionally does not
|
|
8
|
+
include secret payloads, passwords, API keys, connection strings, or `.env`
|
|
9
|
+
contents.
|
|
10
|
+
|
|
11
|
+
## Ownership Rule
|
|
12
|
+
|
|
13
|
+
App-owned runtime/config secrets use:
|
|
14
|
+
|
|
15
|
+
```txt
|
|
16
|
+
hasna/xyz/{app_type}/{app}/prod/{component}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Shared infra/admin pointers use an infra-owned path:
|
|
20
|
+
|
|
21
|
+
```txt
|
|
22
|
+
hasna/xyz/infra/{resource_group}/prod/{component}/{role}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Legacy master credentials copied for migration are suffixed with
|
|
26
|
+
`legacy-master`. They are deprecation inputs for migration jobs, not the clean
|
|
27
|
+
runtime secret names new app code should read.
|
|
28
|
+
|
|
29
|
+
## Open Files
|
|
30
|
+
|
|
31
|
+
Created or verified in AWS Secrets Manager and the local vault:
|
|
32
|
+
|
|
33
|
+
```txt
|
|
34
|
+
hasna/xyz/opensource/files/prod/env
|
|
35
|
+
hasna/xyz/opensource/files/prod/aws
|
|
36
|
+
hasna/xyz/opensource/files/prod/s3
|
|
37
|
+
hasna/xyz/opensource/files/prod/rds
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Meaning:
|
|
41
|
+
|
|
42
|
+
- `env`: app environment configuration.
|
|
43
|
+
- `aws`: AWS account/profile/region and related app config metadata.
|
|
44
|
+
- `s3`: canonical app storage bucket metadata for
|
|
45
|
+
`hasna-xyz-opensource-files-prod`.
|
|
46
|
+
- `rds`: app runtime database connection fields for database `files` and role
|
|
47
|
+
`files_app`.
|
|
48
|
+
|
|
49
|
+
The `aws` and `s3` entries are metadata-only. They do not contain access keys or
|
|
50
|
+
tokens.
|
|
51
|
+
|
|
52
|
+
## Open Knowledge
|
|
53
|
+
|
|
54
|
+
Created or verified in AWS Secrets Manager and the local vault:
|
|
55
|
+
|
|
56
|
+
```txt
|
|
57
|
+
hasna/xyz/opensource/knowledge/prod/env
|
|
58
|
+
hasna/xyz/opensource/knowledge/prod/aws
|
|
59
|
+
hasna/xyz/opensource/knowledge/prod/s3
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Meaning:
|
|
63
|
+
|
|
64
|
+
- `env`: open-knowledge production app config metadata.
|
|
65
|
+
- `aws`: AWS account/profile/region and related app config metadata.
|
|
66
|
+
- `s3`: canonical app storage bucket metadata for
|
|
67
|
+
`hasna-xyz-opensource-knowledge-prod`.
|
|
68
|
+
|
|
69
|
+
No `hasna/xyz/opensource/knowledge/prod/rds` secret was created in this pass.
|
|
70
|
+
The OSS package is local-first and currently uses SQLite for local knowledge
|
|
71
|
+
state. If a hosted wrapper later provisions an app database, the app-owned
|
|
72
|
+
runtime secret should use:
|
|
73
|
+
|
|
74
|
+
```txt
|
|
75
|
+
hasna/xyz/opensource/knowledge/prod/rds
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Legacy Secret Mapping
|
|
79
|
+
|
|
80
|
+
Mapped legacy AWS Secrets Manager names to canonical ownership paths:
|
|
81
|
+
|
|
82
|
+
| Legacy name | Canonical path | Use |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| `prod/microservice/rds/master` | `hasna/xyz/opensource/microservices/prod/rds/legacy-master` | Migration-only legacy master alias. |
|
|
85
|
+
| `prod/connect/rds/master` | `hasna/xyz/opensource/connectors/prod/rds/legacy-master` | Migration-only legacy master alias. |
|
|
86
|
+
| `internalapps/prod/rds/master` | `hasna/xyz/infra/apps/prod/postgres/legacy-internalapps-master` | Migration-only legacy shared/admin alias. |
|
|
87
|
+
| `internalapps/prod/iapp-news/env` | `hasna/xyz/internalapp/news/prod/env` | Canonical app env path for internalapp `news`. |
|
|
88
|
+
|
|
89
|
+
The three RDS aliases preserve old master credential payloads under explicit
|
|
90
|
+
legacy names so migration jobs can read them without relying on noncanonical
|
|
91
|
+
paths. They should not be used as the final runtime credentials for new app
|
|
92
|
+
code. Clean app runtime database credentials should be provisioned under
|
|
93
|
+
app-owned paths such as:
|
|
94
|
+
|
|
95
|
+
```txt
|
|
96
|
+
hasna/xyz/opensource/files/prod/rds
|
|
97
|
+
hasna/xyz/internalapp/news/prod/rds
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The shared canonical Postgres admin pointer remains:
|
|
101
|
+
|
|
102
|
+
```txt
|
|
103
|
+
hasna/xyz/infra/apps/prod/postgres/master
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Verification
|
|
107
|
+
|
|
108
|
+
AWS Secrets Manager name-only verification returned these canonical entries:
|
|
109
|
+
|
|
110
|
+
```txt
|
|
111
|
+
hasna/xyz/infra/apps/prod/postgres/legacy-internalapps-master
|
|
112
|
+
hasna/xyz/infra/apps/prod/postgres/master
|
|
113
|
+
hasna/xyz/internalapp/news/prod/env
|
|
114
|
+
hasna/xyz/opensource/connectors/prod/rds/legacy-master
|
|
115
|
+
hasna/xyz/opensource/files/prod/aws
|
|
116
|
+
hasna/xyz/opensource/files/prod/env
|
|
117
|
+
hasna/xyz/opensource/files/prod/rds
|
|
118
|
+
hasna/xyz/opensource/files/prod/s3
|
|
119
|
+
hasna/xyz/opensource/knowledge/prod/aws
|
|
120
|
+
hasna/xyz/opensource/knowledge/prod/env
|
|
121
|
+
hasna/xyz/opensource/knowledge/prod/s3
|
|
122
|
+
hasna/xyz/opensource/microservices/prod/rds/legacy-master
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Local `secrets list` verification returned the same names with redacted values.
|
|
126
|
+
|
|
127
|
+
No secret values were printed during creation or verification.
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Company Wiki Workflow
|
|
2
|
+
|
|
3
|
+
This workflow shows how to use `open-knowledge` as an AI-native company wiki
|
|
4
|
+
layer over source files owned by `open-files`.
|
|
5
|
+
|
|
6
|
+
## 1. Initialize The Project Workspace
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
open-knowledge paths --scope project --json
|
|
10
|
+
open-knowledge db init --scope project --json
|
|
11
|
+
open-knowledge wiki init --scope project --json
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Project state is created under:
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
.hasna/apps/knowledge/
|
|
18
|
+
config.json
|
|
19
|
+
knowledge.db
|
|
20
|
+
artifacts/
|
|
21
|
+
indexes/
|
|
22
|
+
logs/
|
|
23
|
+
runs/
|
|
24
|
+
schemas/
|
|
25
|
+
wiki/
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 2. Import Source Metadata From Open-Files
|
|
29
|
+
|
|
30
|
+
Use an `open-files` manifest with source refs, revisions, permissions, hashes,
|
|
31
|
+
and extracted text:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"source_ref": "open-files://file/file_handbook/revision/rev_20260608",
|
|
36
|
+
"file_id": "file_handbook",
|
|
37
|
+
"path": "Handbook/Policy.md",
|
|
38
|
+
"name": "Policy.md",
|
|
39
|
+
"mime": "text/markdown",
|
|
40
|
+
"hash": "sha256:...",
|
|
41
|
+
"status": "active",
|
|
42
|
+
"permissions": {
|
|
43
|
+
"mode": "read_only",
|
|
44
|
+
"allowed_purposes": ["knowledge_answer", "knowledge_index"]
|
|
45
|
+
},
|
|
46
|
+
"extracted_text": "Policy text..."
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Then ingest it:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
open-knowledge ingest manifest ./open-files-manifest.jsonl --scope project --json
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The knowledge app stores source refs, revisions, redacted chunks, offsets, and
|
|
57
|
+
citations. It does not store raw source bytes or connector credentials.
|
|
58
|
+
|
|
59
|
+
## 3. Search And Build Context
|
|
60
|
+
|
|
61
|
+
Run local keyword/catalog search first:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
open-knowledge search "expense policy" --scope project --json
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Add deterministic local semantic indexing for a smoke test:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
open-knowledge embeddings index --scope project --fake --dimensions 8 --json
|
|
71
|
+
open-knowledge search "expense policy" --scope project --semantic --fake --dimensions 8 --json
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Ask for an agent-ready context pack:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
open-knowledge search "expense policy" --scope project --context --json
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## 4. Answer With Citations
|
|
81
|
+
|
|
82
|
+
Create a local citation draft:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
knowledge "How do we approve expenses?" --scope project --json
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Use provider generation explicitly:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
OPENAI_API_KEY=... \
|
|
92
|
+
knowledge "How do we approve expenses?" \
|
|
93
|
+
--scope project \
|
|
94
|
+
--generate \
|
|
95
|
+
--model openai:gpt-5-mini \
|
|
96
|
+
--json
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Every prompt run records `runs`, `run_events`, provider/model metadata, usage,
|
|
100
|
+
citations, and proposed wiki updates.
|
|
101
|
+
|
|
102
|
+
## 5. Compile Durable Wiki Pages
|
|
103
|
+
|
|
104
|
+
Compile a cited page from indexed chunks:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
open-knowledge wiki compile "expense policy" \
|
|
108
|
+
--title "Expense Policy" \
|
|
109
|
+
--scope project \
|
|
110
|
+
--json
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
File an approved answer note:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
open-knowledge wiki file-answer "How do we approve expenses?" \
|
|
117
|
+
--content "Use manager approval and cite the policy source." \
|
|
118
|
+
--approve-write \
|
|
119
|
+
--scope project \
|
|
120
|
+
--json
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Lint the generated wiki:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
open-knowledge wiki lint --scope project --json
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Generated pages are written through the artifact store and cataloged in
|
|
130
|
+
`knowledge.db`; index rows and logs are sharded rather than stored in one large
|
|
131
|
+
Markdown file.
|
|
132
|
+
|
|
133
|
+
## 6. Keep Sources Fresh
|
|
134
|
+
|
|
135
|
+
Consume open-files outbox events after source changes:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
open-knowledge reindex outbox ./open-files-outbox.jsonl --scope project --json
|
|
139
|
+
open-knowledge reindex enqueue --scope project --json
|
|
140
|
+
open-knowledge reindex embeddings --scope project --fake --dimensions 8 --json
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
This invalidates stale source chunks and refreshes embeddings without losing the
|
|
144
|
+
source refs and citation provenance.
|
|
145
|
+
|
|
146
|
+
## 7. Expose The Wiki To Agents Through MCP
|
|
147
|
+
|
|
148
|
+
Run MCP over stdio:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
open-knowledge-mcp
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Or run local Streamable HTTP:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
open-knowledge-mcp --http --port 8819
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Agents should prefer stable tools such as `knowledge_search`, `knowledge_ask`,
|
|
161
|
+
`knowledge_build`, `knowledge_get`, `knowledge_lint`, and
|
|
162
|
+
`knowledge_run_status`. They can inspect project resources such as
|
|
163
|
+
`knowledge://project/wiki/pages`, `knowledge://project/runs`, and
|
|
164
|
+
`knowledge://project/open-files`.
|
|
165
|
+
|
|
166
|
+
## 8. Optional Hosted And S3 Mode
|
|
167
|
+
|
|
168
|
+
Hosted mode is only a remote client boundary:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
open-knowledge setup --mode hosted --api-url https://knowledge.hasna.xyz --scope project --json
|
|
172
|
+
open-knowledge remote contracts --scope project --json
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Generated artifacts may use S3 when configured, but raw source files still stay
|
|
176
|
+
in `open-files`.
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# JSON To SQLite Migration
|
|
2
|
+
|
|
3
|
+
`open-knowledge` began as a simple JSON note store. Current project mode uses a
|
|
4
|
+
Hasna app workspace and a versioned SQLite catalog:
|
|
5
|
+
|
|
6
|
+
```text
|
|
7
|
+
.hasna/apps/knowledge/
|
|
8
|
+
db.json
|
|
9
|
+
knowledge.db
|
|
10
|
+
artifacts/
|
|
11
|
+
indexes/
|
|
12
|
+
logs/
|
|
13
|
+
runs/
|
|
14
|
+
schemas/
|
|
15
|
+
wiki/
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The JSON store remains available for compatibility with note commands such as
|
|
19
|
+
`add`, `list`, `get`, `update`, `delete`, and `export`. The SQLite catalog is
|
|
20
|
+
used for source refs, source revisions, chunks, citations, embeddings, wiki
|
|
21
|
+
pages, generated artifacts, runs, audit events, and reindex jobs.
|
|
22
|
+
|
|
23
|
+
## What Migrates Automatically
|
|
24
|
+
|
|
25
|
+
Global legacy notes are migrated on first use:
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
~/.open-knowledge/db.json
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
to:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
~/.hasna/apps/knowledge/db.json
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
This happens only when the new Hasna JSON store does not already exist. The
|
|
38
|
+
legacy file is not deleted.
|
|
39
|
+
|
|
40
|
+
Project mode writes directly to:
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
<project>/.hasna/apps/knowledge/db.json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
when compatibility note commands are used with `--scope project`.
|
|
47
|
+
|
|
48
|
+
## What Requires Explicit Ingestion
|
|
49
|
+
|
|
50
|
+
SQLite knowledge records are not inferred from old JSON notes automatically.
|
|
51
|
+
Use explicit commands so provenance, permissions, citations, and redaction are
|
|
52
|
+
recorded correctly.
|
|
53
|
+
|
|
54
|
+
Initialize the project catalog:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
open-knowledge db init --scope project --json
|
|
58
|
+
open-knowledge wiki init --scope project --json
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Import open-files manifests:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
open-knowledge ingest manifest ./open-files-manifest.jsonl --scope project --json
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Import one allowed source ref:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
open-knowledge ingest source file:///absolute/path/to/handbook.md \
|
|
71
|
+
--purpose knowledge_index \
|
|
72
|
+
--scope project \
|
|
73
|
+
--json
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Resolve indexed source evidence:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
open-knowledge source resolve open-files://file/file_123/revision/rev_456 \
|
|
80
|
+
--purpose knowledge_answer \
|
|
81
|
+
--scope project \
|
|
82
|
+
--json
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Recommended Migration Path
|
|
86
|
+
|
|
87
|
+
1. Keep the legacy JSON note store as an exportable compatibility layer.
|
|
88
|
+
2. Run `open-knowledge paths --scope project --json` and confirm the project
|
|
89
|
+
workspace is `.hasna/apps/knowledge`.
|
|
90
|
+
3. Initialize `knowledge.db` with `open-knowledge db init --scope project`.
|
|
91
|
+
4. Ingest source manifests from `open-files` rather than copying raw files into
|
|
92
|
+
`open-knowledge`.
|
|
93
|
+
5. Run `open-knowledge search --scope project --json` to verify source chunks.
|
|
94
|
+
6. Run `open-knowledge wiki compile` for durable cited pages.
|
|
95
|
+
7. Run `open-knowledge wiki lint --scope project --json` before treating pages
|
|
96
|
+
as company knowledge.
|
|
97
|
+
8. Use `open-knowledge export --format jsonl` if legacy notes need to be
|
|
98
|
+
archived or transformed outside the app.
|
|
99
|
+
|
|
100
|
+
## JSON Output Contracts
|
|
101
|
+
|
|
102
|
+
Use `--json` during migration. Commands return stable objects with `ok: true`
|
|
103
|
+
when successful and command-specific fields such as:
|
|
104
|
+
|
|
105
|
+
- `paths`: workspace paths and config.
|
|
106
|
+
- `db stats`: schema version and table counts.
|
|
107
|
+
- `ingest manifest`: sources, revisions, chunks, redactions, and skipped rows.
|
|
108
|
+
- `source resolve`: read-only source metadata, chunks, citations, and evidence.
|
|
109
|
+
- `search --context`: excerpts, citations, graph evidence, and warnings.
|
|
110
|
+
- `ask|build`: run id, answer, context, citations, proposed wiki updates, write
|
|
111
|
+
policy, usage, and warnings.
|
|
112
|
+
- `wiki compile`: page id, artifact URI, citations written, index updates, and
|
|
113
|
+
log shard key.
|
|
114
|
+
|
|
115
|
+
## Safety Rules During Migration
|
|
116
|
+
|
|
117
|
+
- Prefer `open-files://` refs for durable company sources.
|
|
118
|
+
- Keep raw source bytes in `open-files`; do not import them as generated wiki
|
|
119
|
+
artifacts.
|
|
120
|
+
- Enable S3 reads only for allowed buckets:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
HASNA_KNOWLEDGE_ALLOW_S3_READS=1 \
|
|
124
|
+
HASNA_KNOWLEDGE_ALLOWED_S3_BUCKETS=my-bucket \
|
|
125
|
+
open-knowledge ingest manifest s3://my-bucket/path/manifest.jsonl \
|
|
126
|
+
--scope project \
|
|
127
|
+
--json
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
- Enable web search only when current external context is required:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
HASNA_KNOWLEDGE_WEB_SEARCH=1 \
|
|
134
|
+
open-knowledge web search "current policy source" --provider openai --json
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
- Use `--approve-write` only when a generated wiki artifact should be durable.
|
|
138
|
+
|
|
139
|
+
## Hosted Migration
|
|
140
|
+
|
|
141
|
+
Hosted mode should not change local migration semantics. It only records a
|
|
142
|
+
remote API boundary:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
open-knowledge setup --mode hosted --api-url https://knowledge.hasna.xyz --scope project --json
|
|
146
|
+
open-knowledge remote contracts --scope project --json
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
A SaaS wrapper can later sync generated artifacts, run jobs, enforce tenant ACLs,
|
|
150
|
+
and store artifacts in S3, but the local package remains usable without a hosted
|
|
151
|
+
account.
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -57,6 +57,7 @@ interface Flags {
|
|
|
57
57
|
provider?: string;
|
|
58
58
|
mode?: string;
|
|
59
59
|
apiUrl?: string;
|
|
60
|
+
canonicalHasnaXyz?: boolean;
|
|
60
61
|
apiKey?: string;
|
|
61
62
|
email?: string;
|
|
62
63
|
org?: string;
|
|
@@ -125,6 +126,7 @@ function parseArgs(argv: string[]): ParseResult {
|
|
|
125
126
|
case '--provider': flags.provider = argv[i + 1]; i += 1; break;
|
|
126
127
|
case '--mode': flags.mode = argv[i + 1]; i += 1; break;
|
|
127
128
|
case '--api-url': flags.apiUrl = argv[i + 1]; i += 1; break;
|
|
129
|
+
case '--canonical-hasna-xyz': flags.canonicalHasnaXyz = true; break;
|
|
128
130
|
case '--api-key': flags.apiKey = argv[i + 1]; i += 1; break;
|
|
129
131
|
case '--email': flags.email = argv[i + 1]; i += 1; break;
|
|
130
132
|
case '--org': flags.org = argv[i + 1]; i += 1; break;
|
|
@@ -204,7 +206,7 @@ Commands:
|
|
|
204
206
|
dedupe Remove duplicate items by title+content (requires --yes)
|
|
205
207
|
stats Show knowledge base statistics
|
|
206
208
|
paths Show resolved workspace/store paths
|
|
207
|
-
setup Configure local or
|
|
209
|
+
setup Configure local, hosted, or canonical Hasna XYZ S3 mode
|
|
208
210
|
auth login|whoami|logout Manage hosted API credentials
|
|
209
211
|
remote contracts|status Inspect hosted client contracts/readiness
|
|
210
212
|
storage status|validate Inspect local/S3 artifact storage contract
|
|
@@ -299,7 +301,7 @@ function printCommandHelp(command: string): void {
|
|
|
299
301
|
if (command === 'dedupe') { console.log('Usage: open-knowledge dedupe --yes [--json]'); return; }
|
|
300
302
|
if (command === 'stats') { console.log('Usage: open-knowledge stats [--json]'); return; }
|
|
301
303
|
if (command === 'paths') { console.log('Usage: open-knowledge paths [--scope local|global|project] [--json]'); return; }
|
|
302
|
-
if (command === 'setup') { console.log('Usage: open-knowledge setup --mode local|hosted [--api-url https://...] [--scope local|global|project] [--json]'); return; }
|
|
304
|
+
if (command === 'setup') { console.log('Usage: open-knowledge setup --mode local|hosted [--api-url https://...] [--canonical-hasna-xyz] [--scope local|global|project] [--json]'); return; }
|
|
303
305
|
if (command === 'auth') { console.log('Usage: open-knowledge auth login|whoami|logout [--api-key <key>] [--email <email>] [--org <slug>] [--api-url https://...] [--scope local|global|project] [--json]'); return; }
|
|
304
306
|
if (command === 'remote') { console.log('Usage: open-knowledge remote contracts|status [--scope local|global|project] [--json]'); return; }
|
|
305
307
|
if (command === 'storage') { console.log('Usage: open-knowledge storage status|validate [--scope local|global|project] [--json]'); return; }
|
|
@@ -358,11 +360,11 @@ async function run(argv: string[]): Promise<void> {
|
|
|
358
360
|
if (flags.completions) {
|
|
359
361
|
const shell = flags.completions;
|
|
360
362
|
if (shell === 'bash') {
|
|
361
|
-
console.log(`_open_knowledge() { local cur; cur="${"$"}{COMP_WORDS[COMP_CWORD]}"; COMPREPLY=($(compgen -W "add list get update archive restore upsert untag delete export prune dedupe stats paths setup auth remote storage db wiki source ingest reindex search web ask build embeddings providers safety help ls rm edit unarchive knowledge --json --yes --help --version --desc --page --limit --search --sort --id --store --title --content --url --tag --format --completions --purpose --model --dimensions --semantic --context --generate --approve-write --provider --mode --api-url --api-key --email --org --org-id --user-id --domain --file-results --full --fake --no-color --scope --archived --include-archived" -- "$cur")); }; complete -F _open_knowledge open-knowledge`);
|
|
363
|
+
console.log(`_open_knowledge() { local cur; cur="${"$"}{COMP_WORDS[COMP_CWORD]}"; COMPREPLY=($(compgen -W "add list get update archive restore upsert untag delete export prune dedupe stats paths setup auth remote storage db wiki source ingest reindex search web ask build embeddings providers safety help ls rm edit unarchive knowledge --json --yes --help --version --desc --page --limit --search --sort --id --store --title --content --url --tag --format --completions --purpose --model --dimensions --semantic --context --generate --approve-write --provider --mode --api-url --canonical-hasna-xyz --api-key --email --org --org-id --user-id --domain --file-results --full --fake --no-color --scope --archived --include-archived" -- "$cur")); }; complete -F _open_knowledge open-knowledge`);
|
|
362
364
|
} else if (shell === 'zsh') {
|
|
363
|
-
console.log(`#compdef open-knowledge\n_open_knowledge() { _arguments -C "1: :(add list get update archive restore upsert untag delete export prune dedupe stats paths setup auth remote storage db wiki source ingest reindex search web ask build embeddings providers safety help ls rm edit unarchive knowledge)" "(--json)--json" "(--yes)-y" "(--help)--help" "(--version)--version" "(--desc)--desc" "(--archived)--archived" "(--include-archived)--include-archived" "(--semantic)--semantic" "(--context)--context" "(--generate)--generate" "(--approve-write)--approve-write" "(--file-results)--file-results" "(--full)--full" "(--fake)--fake" "(-p --page)"{-p,--page}"[page number]:number:" "(-l --limit)"{-l,--limit}"[items per page]:number:" "(-s --search)"{-s,--search}"[search text]:text:" "(--sort)--sort"\{created,title\}:" "(--id)--id[item id]:id:" "(--store)--store[store path]:path:" "(--title)--title[new title]:" "(--content)--content[new content]:" "(--url)--url[source url]:" "(-t --tag)"{-t,--tag}"[tag]:tag:" "(--format)--format[json|jsonl]:" "(--completions)--completions[output completions]:shell:(bash zsh fish):" "(--purpose)--purpose[purpose]:" "(--model)--model[model ref]:" "(--dimensions)--dimensions[embedding dimensions]:number:" "(--provider)--provider[provider]:" "(--mode)--mode"\{local,hosted\}:" "(--api-url)--api-url[hosted API URL]:" "(--api-key)--api-key[hosted API key]:" "(--email)--email[email]:" "(--org)--org[org slug]:" "(--org-id)--org-id[org id]:" "(--user-id)--user-id[user id]:" "(--domain)--domain[domain]:" "(--no-color)--no-color[disable color]" "(--scope)--scope"\{local,global,project\}:" }; _open_knowledge`);
|
|
365
|
+
console.log(`#compdef open-knowledge\n_open_knowledge() { _arguments -C "1: :(add list get update archive restore upsert untag delete export prune dedupe stats paths setup auth remote storage db wiki source ingest reindex search web ask build embeddings providers safety help ls rm edit unarchive knowledge)" "(--json)--json" "(--yes)-y" "(--help)--help" "(--version)--version" "(--desc)--desc" "(--archived)--archived" "(--include-archived)--include-archived" "(--semantic)--semantic" "(--context)--context" "(--generate)--generate" "(--approve-write)--approve-write" "(--canonical-hasna-xyz)--canonical-hasna-xyz" "(--file-results)--file-results" "(--full)--full" "(--fake)--fake" "(-p --page)"{-p,--page}"[page number]:number:" "(-l --limit)"{-l,--limit}"[items per page]:number:" "(-s --search)"{-s,--search}"[search text]:text:" "(--sort)--sort"\{created,title\}:" "(--id)--id[item id]:id:" "(--store)--store[store path]:path:" "(--title)--title[new title]:" "(--content)--content[new content]:" "(--url)--url[source url]:" "(-t --tag)"{-t,--tag}"[tag]:tag:" "(--format)--format[json|jsonl]:" "(--completions)--completions[output completions]:shell:(bash zsh fish):" "(--purpose)--purpose[purpose]:" "(--model)--model[model ref]:" "(--dimensions)--dimensions[embedding dimensions]:number:" "(--provider)--provider[provider]:" "(--mode)--mode"\{local,hosted\}:" "(--api-url)--api-url[hosted API URL]:" "(--api-key)--api-key[hosted API key]:" "(--email)--email[email]:" "(--org)--org[org slug]:" "(--org-id)--org-id[org id]:" "(--user-id)--user-id[user id]:" "(--domain)--domain[domain]:" "(--no-color)--no-color[disable color]" "(--scope)--scope"\{local,global,project\}:" }; _open_knowledge`);
|
|
364
366
|
} else if (shell === 'fish') {
|
|
365
|
-
console.log(`complete -c open-knowledge -f; complete -c open-knowledge -a "add list get update archive restore upsert untag delete export prune dedupe stats paths setup auth remote storage db wiki source ingest reindex search web ask build embeddings providers safety help ls rm edit unarchive knowledge"; complete -c open-knowledge -l json; complete -c open-knowledge -l yes -s y; complete -c open-knowledge -l help -s h; complete -c open-knowledge -l version -s v; complete -c open-knowledge -l desc; complete -c open-knowledge -l archived; complete -c open-knowledge -l include-archived; complete -c open-knowledge -l semantic; complete -c open-knowledge -l context; complete -c open-knowledge -l generate; complete -c open-knowledge -l approve-write; complete -c open-knowledge -l provider; complete -c open-knowledge -l mode; complete -c open-knowledge -l api-url; complete -c open-knowledge -l api-key; complete -c open-knowledge -l email; complete -c open-knowledge -l org; complete -c open-knowledge -l org-id; complete -c open-knowledge -l user-id; complete -c open-knowledge -l domain; complete -c open-knowledge -l file-results; complete -c open-knowledge -l full; complete -c open-knowledge -l fake; complete -c open-knowledge -s p -l page; complete -c open-knowledge -s l -l limit; complete -c open-knowledge -s s -l search; complete -c open-knowledge -l sort; complete -c open-knowledge -l id; complete -c open-knowledge -l store; complete -c open-knowledge -l title; complete -c open-knowledge -l content; complete -c open-knowledge -l url; complete -c open-knowledge -s t -l tag; complete -c open-knowledge -l format; complete -c open-knowledge -l completions; complete -c open-knowledge -l purpose; complete -c open-knowledge -l model; complete -c open-knowledge -l dimensions; complete -c open-knowledge -l no-color; complete -c open-knowledge -l scope -a "local global project"`);
|
|
367
|
+
console.log(`complete -c open-knowledge -f; complete -c open-knowledge -a "add list get update archive restore upsert untag delete export prune dedupe stats paths setup auth remote storage db wiki source ingest reindex search web ask build embeddings providers safety help ls rm edit unarchive knowledge"; complete -c open-knowledge -l json; complete -c open-knowledge -l yes -s y; complete -c open-knowledge -l help -s h; complete -c open-knowledge -l version -s v; complete -c open-knowledge -l desc; complete -c open-knowledge -l archived; complete -c open-knowledge -l include-archived; complete -c open-knowledge -l semantic; complete -c open-knowledge -l context; complete -c open-knowledge -l generate; complete -c open-knowledge -l approve-write; complete -c open-knowledge -l canonical-hasna-xyz; complete -c open-knowledge -l provider; complete -c open-knowledge -l mode; complete -c open-knowledge -l api-url; complete -c open-knowledge -l api-key; complete -c open-knowledge -l email; complete -c open-knowledge -l org; complete -c open-knowledge -l org-id; complete -c open-knowledge -l user-id; complete -c open-knowledge -l domain; complete -c open-knowledge -l file-results; complete -c open-knowledge -l full; complete -c open-knowledge -l fake; complete -c open-knowledge -s p -l page; complete -c open-knowledge -s l -l limit; complete -c open-knowledge -s s -l search; complete -c open-knowledge -l sort; complete -c open-knowledge -l id; complete -c open-knowledge -l store; complete -c open-knowledge -l title; complete -c open-knowledge -l content; complete -c open-knowledge -l url; complete -c open-knowledge -s t -l tag; complete -c open-knowledge -l format; complete -c open-knowledge -l completions; complete -c open-knowledge -l purpose; complete -c open-knowledge -l model; complete -c open-knowledge -l dimensions; complete -c open-knowledge -l no-color; complete -c open-knowledge -l scope -a "local global project"`);
|
|
366
368
|
} else {
|
|
367
369
|
throw new Error("Invalid --completions value. Use 'bash', 'zsh', or 'fish'.");
|
|
368
370
|
}
|
|
@@ -397,6 +399,7 @@ async function run(argv: string[]): Promise<void> {
|
|
|
397
399
|
const result = service.setup({
|
|
398
400
|
mode: flags.mode,
|
|
399
401
|
apiUrl: flags.apiUrl,
|
|
402
|
+
canonicalHasnaXyz: flags.canonicalHasnaXyz,
|
|
400
403
|
});
|
|
401
404
|
output(result, flags.json);
|
|
402
405
|
return;
|
package/src/service.ts
CHANGED
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
} from './storage-contract';
|
|
37
37
|
import { initializeWikiLayout, recordWikiLayoutCatalog } from './wiki-layout';
|
|
38
38
|
import {
|
|
39
|
+
canonicalHasnaXyzKnowledgeStorage,
|
|
39
40
|
ensureKnowledgeWorkspace,
|
|
40
41
|
readKnowledgeConfig,
|
|
41
42
|
resolveScopedWorkspace,
|
|
@@ -70,6 +71,9 @@ export interface KnowledgeSetupResult {
|
|
|
70
71
|
ok: true;
|
|
71
72
|
mode: KnowledgeConfig['mode'];
|
|
72
73
|
api_url: string | null;
|
|
74
|
+
storage_type: KnowledgeConfig['storage']['type'];
|
|
75
|
+
artifact_uri_prefix: string;
|
|
76
|
+
canonical_hasna_xyz: StorageContract['canonical_hasna_xyz'];
|
|
73
77
|
config_path: string;
|
|
74
78
|
next: string[];
|
|
75
79
|
message: string;
|
|
@@ -130,7 +134,7 @@ export class KnowledgeService {
|
|
|
130
134
|
return validateStorageConfig(this.config(), this.ensureWorkspace());
|
|
131
135
|
}
|
|
132
136
|
|
|
133
|
-
setup(options: { mode?: string; apiUrl?: string } = {}): KnowledgeSetupResult {
|
|
137
|
+
setup(options: { mode?: string; apiUrl?: string; canonicalHasnaXyz?: boolean } = {}): KnowledgeSetupResult {
|
|
134
138
|
const workspace = this.ensureWorkspace();
|
|
135
139
|
const current = this.config();
|
|
136
140
|
const mode = normalizeMode(options.mode) ?? current.mode;
|
|
@@ -146,16 +150,23 @@ export class KnowledgeService {
|
|
|
146
150
|
...(current.hosted ?? {}),
|
|
147
151
|
...(apiUrl ? { api_url: apiUrl } : {}),
|
|
148
152
|
},
|
|
153
|
+
storage: options.canonicalHasnaXyz
|
|
154
|
+
? canonicalHasnaXyzKnowledgeStorage()
|
|
155
|
+
: current.storage,
|
|
149
156
|
};
|
|
150
157
|
writeKnowledgeConfig(workspace.configPath, nextConfig);
|
|
151
158
|
this.cachedConfig = nextConfig;
|
|
159
|
+
const storage = resolveStorageContract(nextConfig, workspace, this.scope);
|
|
152
160
|
return {
|
|
153
161
|
ok: true,
|
|
154
162
|
mode,
|
|
155
163
|
api_url: nextConfig.hosted?.api_url ?? null,
|
|
164
|
+
storage_type: nextConfig.storage.type,
|
|
165
|
+
artifact_uri_prefix: storage.artifact_store.uri_prefix,
|
|
166
|
+
canonical_hasna_xyz: storage.canonical_hasna_xyz,
|
|
156
167
|
config_path: workspace.configPath,
|
|
157
168
|
next: mode === 'hosted'
|
|
158
|
-
? ['open-knowledge auth login --api-key <key>', 'open-knowledge remote contracts --json']
|
|
169
|
+
? ['open-knowledge auth login --api-key <key>', 'open-knowledge storage status --json', 'open-knowledge remote contracts --json']
|
|
159
170
|
: ['open-knowledge search <query>', 'knowledge <prompt>'],
|
|
160
171
|
message: `Set knowledge mode to ${mode}`,
|
|
161
172
|
};
|