@hasna/knowledge 0.2.20 → 0.2.21

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 CHANGED
@@ -65,6 +65,11 @@ open-knowledge paths --scope project --json
65
65
  # Inspect local/S3 artifact storage and source ownership
66
66
  open-knowledge storage status --scope project --json
67
67
 
68
+ # Configure optional hosted mode and inspect remote contracts
69
+ open-knowledge setup --mode hosted --api-url https://knowledge.hasna.xyz --scope project --json
70
+ open-knowledge auth whoami --scope project --json
71
+ open-knowledge remote contracts --scope project --json
72
+
68
73
  # Initialize the project SQLite catalog
69
74
  open-knowledge db init --scope project
70
75
 
@@ -204,6 +209,23 @@ knowledge bucket/prefix while `open-files` remains the source of truth for raw
204
209
  source bytes. The command also reports artifact classes, allowed source ref
205
210
  schemes, and warnings for non-scalable or unsafe config.
206
211
 
212
+ ### setup / auth / remote
213
+ ```bash
214
+ open-knowledge setup --mode local [--scope project] [--json]
215
+ open-knowledge setup --mode hosted [--api-url https://knowledge.hasna.xyz] [--scope project] [--json]
216
+ open-knowledge auth login --api-key <key> [--email you@example.com] [--org <slug>] [--scope project] [--json]
217
+ open-knowledge auth whoami [--scope project] [--json]
218
+ open-knowledge auth logout [--scope project] [--json]
219
+ open-knowledge remote status [--scope project] [--json]
220
+ open-knowledge remote contracts [--scope project] [--json]
221
+ ```
222
+ Hosted mode mirrors the `open-skills` open-core pattern: the OSS package stays
223
+ local-first, while `hosted.api_url`, `KNOWLEDGE_API_URL`, and
224
+ `KNOWLEDGE_API_KEY` define an optional remote client boundary. Credentials are
225
+ stored locally in `~/.hasna/knowledge/auth.json` or supplied by env vars.
226
+ `remote contracts` prints the typed registry/search/ask/build/sync/status/logs
227
+ and artifact API contract that a future SaaS wrapper can implement.
228
+
207
229
  ### db
208
230
  ```bash
209
231
  open-knowledge db init [--scope project]
@@ -13656,11 +13656,11 @@ function date4(params) {
13656
13656
  // node_modules/zod/v4/classic/external.js
13657
13657
  config(en_default());
13658
13658
  // src/mcp.js
13659
- import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
13659
+ import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
13660
13660
  // package.json
13661
13661
  var package_default = {
13662
13662
  name: "@hasna/knowledge",
13663
- version: "0.2.20",
13663
+ version: "0.2.21",
13664
13664
  description: "Agent-friendly local knowledge CLI with JSON output, pagination, and safe destructive actions",
13665
13665
  type: "module",
13666
13666
  bin: {
@@ -13761,6 +13761,9 @@ function defaultKnowledgeConfig() {
13761
13761
  return {
13762
13762
  version: 1,
13763
13763
  mode: "local",
13764
+ hosted: {
13765
+ api_url: "https://knowledge.hasna.xyz"
13766
+ },
13764
13767
  storage: {
13765
13768
  type: "local",
13766
13769
  artifacts_root: "artifacts"
@@ -13846,6 +13849,11 @@ function readKnowledgeConfig(path) {
13846
13849
  const raw = readFileSync(path, "utf8");
13847
13850
  return JSON.parse(raw);
13848
13851
  }
13852
+ function writeKnowledgeConfig(path, config2) {
13853
+ ensureParentDir(path);
13854
+ writeFileSync(path, `${JSON.stringify(config2, null, 2)}
13855
+ `);
13856
+ }
13849
13857
 
13850
13858
  // src/store.ts
13851
13859
  function defaultStorePath() {
@@ -14135,6 +14143,91 @@ function createArtifactStore(config2, workspace) {
14135
14143
  return new LocalArtifactStore(workspace.artifactsDir);
14136
14144
  }
14137
14145
 
14146
+ // src/auth.ts
14147
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
14148
+ import { homedir as homedir2 } from "os";
14149
+ import { dirname as dirname3, join as join3 } from "path";
14150
+ var DEFAULT_KNOWLEDGE_API_URL = "https://knowledge.hasna.xyz";
14151
+ function normalizeKnowledgeApiOrigin(apiUrl) {
14152
+ const url2 = new URL(apiUrl);
14153
+ if (url2.protocol !== "http:" && url2.protocol !== "https:") {
14154
+ throw new Error("Knowledge API URL must use http or https.");
14155
+ }
14156
+ const pathname = url2.pathname.replace(/\/+$/, "");
14157
+ if (pathname === "/api" || pathname === "/api/v1") {
14158
+ url2.pathname = "/";
14159
+ } else if (pathname.endsWith("/api/v1")) {
14160
+ url2.pathname = pathname.slice(0, -"/api/v1".length) || "/";
14161
+ } else if (pathname.endsWith("/api")) {
14162
+ url2.pathname = pathname.slice(0, -"/api".length) || "/";
14163
+ }
14164
+ return url2.toString().replace(/\/+$/, "");
14165
+ }
14166
+ function knowledgeAuthPath(env = process.env) {
14167
+ if (env.HASNA_KNOWLEDGE_AUTH_PATH)
14168
+ return env.HASNA_KNOWLEDGE_AUTH_PATH;
14169
+ const root = env.HASNA_KNOWLEDGE_AUTH_DIR ?? join3(homedir2(), ".hasna", "knowledge");
14170
+ return join3(root, "auth.json");
14171
+ }
14172
+ function resolveKnowledgeApiUrl(config2, env = process.env) {
14173
+ return normalizeKnowledgeApiOrigin(env.KNOWLEDGE_API_URL ?? config2?.hosted?.api_url ?? DEFAULT_KNOWLEDGE_API_URL);
14174
+ }
14175
+ function getKnowledgeAuth(env = process.env) {
14176
+ try {
14177
+ const path = knowledgeAuthPath(env);
14178
+ if (!existsSync4(path))
14179
+ return null;
14180
+ const parsed = JSON.parse(readFileSync4(path, "utf8"));
14181
+ return typeof parsed.api_key === "string" && parsed.api_key.length > 0 ? parsed : null;
14182
+ } catch {
14183
+ return null;
14184
+ }
14185
+ }
14186
+ function saveKnowledgeAuth(auth, env = process.env) {
14187
+ const path = knowledgeAuthPath(env);
14188
+ const stored = {
14189
+ ...auth,
14190
+ api_url: auth.api_url ? normalizeKnowledgeApiOrigin(auth.api_url) : undefined,
14191
+ created_at: auth.created_at ?? new Date().toISOString()
14192
+ };
14193
+ mkdirSync3(dirname3(path), { recursive: true, mode: 448 });
14194
+ writeFileSync4(path, `${JSON.stringify(stored, null, 2)}
14195
+ `, { mode: 384 });
14196
+ return stored;
14197
+ }
14198
+ function clearKnowledgeAuth(env = process.env) {
14199
+ try {
14200
+ unlinkSync2(knowledgeAuthPath(env));
14201
+ return true;
14202
+ } catch {
14203
+ return false;
14204
+ }
14205
+ }
14206
+ function getKnowledgeApiKey(env = process.env) {
14207
+ if (env.KNOWLEDGE_API_KEY)
14208
+ return { apiKey: env.KNOWLEDGE_API_KEY, source: "env" };
14209
+ if (env.HASNA_KNOWLEDGE_API_KEY)
14210
+ return { apiKey: env.HASNA_KNOWLEDGE_API_KEY, source: "env" };
14211
+ const auth = getKnowledgeAuth(env);
14212
+ return auth?.api_key ? { apiKey: auth.api_key, source: "file" } : { apiKey: null, source: "none" };
14213
+ }
14214
+ function knowledgeAuthStatus(config2, env = process.env) {
14215
+ const auth = getKnowledgeAuth(env);
14216
+ const key = getKnowledgeApiKey(env);
14217
+ const apiUrl = env.KNOWLEDGE_API_URL ? resolveKnowledgeApiUrl(config2, env) : auth?.api_url ? normalizeKnowledgeApiOrigin(auth.api_url) : resolveKnowledgeApiUrl(config2, env);
14218
+ return {
14219
+ authenticated: Boolean(key.apiKey),
14220
+ source: key.source,
14221
+ api_url: apiUrl,
14222
+ auth_path: knowledgeAuthPath(env),
14223
+ email: key.source === "file" ? auth?.email ?? null : null,
14224
+ org_id: key.source === "file" ? auth?.org_id ?? null : null,
14225
+ org_slug: key.source === "file" ? auth?.org_slug ?? null : null,
14226
+ user_id: key.source === "file" ? auth?.user_id ?? null : null,
14227
+ api_key_present: Boolean(key.apiKey)
14228
+ };
14229
+ }
14230
+
14138
14231
  // src/agent.ts
14139
14232
  import { randomUUID as randomUUID3 } from "crypto";
14140
14233
 
@@ -15930,7 +16023,7 @@ ${answer}`;
15930
16023
 
15931
16024
  // src/outbox-consume.ts
15932
16025
  import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
15933
- import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
16026
+ import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
15934
16027
  import { basename } from "path";
15935
16028
 
15936
16029
  // src/safety.ts
@@ -16203,9 +16296,9 @@ async function readS3Text(uri, config2, safetyPolicy) {
16203
16296
  async function readOutboxInput(input, config2, safetyPolicy) {
16204
16297
  if (input.startsWith("s3://"))
16205
16298
  return readS3Text(input, config2, safetyPolicy);
16206
- if (!existsSync4(input))
16299
+ if (!existsSync5(input))
16207
16300
  throw new Error(`Outbox not found: ${input}`);
16208
- return readFileSync4(input, "utf8");
16301
+ return readFileSync5(input, "utf8");
16209
16302
  }
16210
16303
  function mergeJson(existing, patch) {
16211
16304
  let base = {};
@@ -16439,7 +16532,7 @@ async function consumeOpenFilesOutbox(options) {
16439
16532
 
16440
16533
  // src/manifest-ingest.ts
16441
16534
  import { createHash as createHash5 } from "crypto";
16442
- import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
16535
+ import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
16443
16536
  import { basename as basename2 } from "path";
16444
16537
  function stableId4(prefix, value) {
16445
16538
  return `${prefix}_${createHash5("sha256").update(value).digest("hex").slice(0, 20)}`;
@@ -16611,9 +16704,9 @@ async function readS3Text2(uri, config2, safetyPolicy) {
16611
16704
  async function readManifestInput(input, config2, safetyPolicy) {
16612
16705
  if (input.startsWith("s3://"))
16613
16706
  return readS3Text2(input, config2, safetyPolicy);
16614
- if (!existsSync5(input))
16707
+ if (!existsSync6(input))
16615
16708
  throw new Error(`Manifest not found: ${input}`);
16616
- return readFileSync5(input, "utf8");
16709
+ return readFileSync6(input, "utf8");
16617
16710
  }
16618
16711
  function chunkText(text, maxChars, overlapChars) {
16619
16712
  const normalized = text.replace(/\r\n/g, `
@@ -16857,7 +16950,7 @@ async function ingestOpenFilesManifestItems(options) {
16857
16950
 
16858
16951
  // src/source-ingest.ts
16859
16952
  import { createHash as createHash6 } from "crypto";
16860
- import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
16953
+ import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
16861
16954
  import { basename as basename3 } from "path";
16862
16955
 
16863
16956
  // src/source-resolver.ts
@@ -17207,9 +17300,9 @@ function titleForRef(parsed) {
17207
17300
  }
17208
17301
  async function readDirectSourceText(parsed, config2, safetyPolicy) {
17209
17302
  if (parsed.kind === "file") {
17210
- if (!existsSync6(parsed.path))
17303
+ if (!existsSync7(parsed.path))
17211
17304
  throw new Error(`Source file not found: ${parsed.path}`);
17212
- const text = readFileSync6(parsed.path, "utf8");
17305
+ const text = readFileSync7(parsed.path, "utf8");
17213
17306
  return {
17214
17307
  text,
17215
17308
  contentSource: "file",
@@ -17560,6 +17653,164 @@ async function refreshEmbeddingIndex(options) {
17560
17653
  };
17561
17654
  }
17562
17655
 
17656
+ // src/remote-client.ts
17657
+ var REMOTE_KNOWLEDGE_CONTRACT_VERSION = 1;
17658
+ function isRecord(value) {
17659
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
17660
+ }
17661
+ function stringValue(record2, key) {
17662
+ const value = record2[key];
17663
+ return typeof value === "string" ? value : undefined;
17664
+ }
17665
+ function numberValue(record2, key) {
17666
+ const value = record2[key];
17667
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
17668
+ }
17669
+ function arrayValue(record2, key) {
17670
+ const value = record2[key];
17671
+ return Array.isArray(value) ? value : undefined;
17672
+ }
17673
+ function normalizeRemoteKnowledgeRunContract(payload, fallback) {
17674
+ const record2 = isRecord(payload) ? payload : {};
17675
+ return {
17676
+ contract_version: REMOTE_KNOWLEDGE_CONTRACT_VERSION,
17677
+ id: stringValue(record2, "id") ?? fallback?.id,
17678
+ type: stringValue(record2, "type") ?? fallback?.type,
17679
+ status: stringValue(record2, "status") ?? fallback?.status,
17680
+ query: stringValue(record2, "query") ?? fallback?.query,
17681
+ prompt: stringValue(record2, "prompt") ?? fallback?.prompt,
17682
+ output_preview: Object.prototype.hasOwnProperty.call(record2, "output_preview") ? record2.output_preview : fallback?.output_preview,
17683
+ citations: arrayValue(record2, "citations") ?? fallback?.citations,
17684
+ artifacts: arrayValue(record2, "artifacts") ?? fallback?.artifacts,
17685
+ usage: isRecord(record2.usage) ? record2.usage : fallback?.usage,
17686
+ created_at: stringValue(record2, "created_at") ?? fallback?.created_at,
17687
+ started_at: stringValue(record2, "started_at") ?? fallback?.started_at,
17688
+ completed_at: stringValue(record2, "completed_at") ?? fallback?.completed_at,
17689
+ duration_ms: numberValue(record2, "duration_ms") ?? fallback?.duration_ms,
17690
+ error_code: stringValue(record2, "error_code") ?? fallback?.error_code,
17691
+ error_message: stringValue(record2, "error_message") ?? fallback?.error_message,
17692
+ error: stringValue(record2, "error") ?? fallback?.error,
17693
+ details: Object.prototype.hasOwnProperty.call(record2, "details") ? record2.details : fallback?.details
17694
+ };
17695
+ }
17696
+ function knowledgeRegistryContract(input) {
17697
+ return {
17698
+ contract_version: REMOTE_KNOWLEDGE_CONTRACT_VERSION,
17699
+ service: "open-knowledge",
17700
+ mode: input.mode,
17701
+ capabilities: [
17702
+ "registry",
17703
+ "search",
17704
+ "ask",
17705
+ "build",
17706
+ "sync",
17707
+ "status",
17708
+ "logs",
17709
+ "artifacts",
17710
+ "open-files-source-refs",
17711
+ "s3-generated-artifacts"
17712
+ ],
17713
+ endpoints: {
17714
+ registry: "/api/v1/knowledge/registry",
17715
+ search: "/api/v1/knowledge/search",
17716
+ ask: "/api/v1/knowledge/ask",
17717
+ build: "/api/v1/knowledge/build",
17718
+ sync: "/api/v1/knowledge/sync",
17719
+ run_status: "/api/v1/knowledge/runs/{run_id}",
17720
+ run_logs: "/api/v1/knowledge/runs/{run_id}/logs",
17721
+ run_artifacts: "/api/v1/knowledge/runs/{run_id}/artifacts"
17722
+ },
17723
+ source_contract: {
17724
+ owner: "open-files",
17725
+ preferred_ref: "open-files",
17726
+ allowed_schemes: input.sourceSchemes,
17727
+ raw_source_bytes_stored_in_open_knowledge: false
17728
+ },
17729
+ artifact_contract: {
17730
+ storage_type: input.storageType,
17731
+ uri_prefix: input.artifactUriPrefix,
17732
+ generated_only: true
17733
+ }
17734
+ };
17735
+ }
17736
+
17737
+ class RemoteKnowledgeClient {
17738
+ apiKey;
17739
+ apiUrl;
17740
+ constructor(apiKey, apiUrl) {
17741
+ this.apiKey = apiKey;
17742
+ this.apiUrl = apiUrl;
17743
+ }
17744
+ static fromConfig(config2, env = process.env) {
17745
+ const key = getKnowledgeApiKey(env);
17746
+ if (!key.apiKey)
17747
+ return null;
17748
+ return new RemoteKnowledgeClient(key.apiKey, resolveKnowledgeApiUrl(config2, env));
17749
+ }
17750
+ async request(path, options = {}) {
17751
+ return fetch(`${this.apiUrl}${path}`, {
17752
+ ...options,
17753
+ headers: {
17754
+ Authorization: `Bearer ${this.apiKey}`,
17755
+ "Content-Type": "application/json",
17756
+ ...options.headers
17757
+ }
17758
+ });
17759
+ }
17760
+ async registry() {
17761
+ const response = await this.request("/api/v1/knowledge/registry");
17762
+ return response.json();
17763
+ }
17764
+ async search(request) {
17765
+ const response = await this.request("/api/v1/knowledge/search", {
17766
+ method: "POST",
17767
+ body: JSON.stringify(request)
17768
+ });
17769
+ return normalizeRemoteKnowledgeRunContract(await response.json(), { type: "search", query: request.query });
17770
+ }
17771
+ async ask(request) {
17772
+ const response = await this.request("/api/v1/knowledge/ask", {
17773
+ method: "POST",
17774
+ body: JSON.stringify(request)
17775
+ });
17776
+ return normalizeRemoteKnowledgeRunContract(await response.json(), { type: "ask", prompt: request.prompt });
17777
+ }
17778
+ async build(request) {
17779
+ const response = await this.request("/api/v1/knowledge/build", {
17780
+ method: "POST",
17781
+ body: JSON.stringify(request)
17782
+ });
17783
+ return normalizeRemoteKnowledgeRunContract(await response.json(), { type: "build", prompt: request.prompt });
17784
+ }
17785
+ async sync(request = {}) {
17786
+ const response = await this.request("/api/v1/knowledge/sync", {
17787
+ method: "POST",
17788
+ body: JSON.stringify(request)
17789
+ });
17790
+ return normalizeRemoteKnowledgeRunContract(await response.json(), { type: "sync" });
17791
+ }
17792
+ async runStatus(runId) {
17793
+ const response = await this.request(`/api/v1/knowledge/runs/${encodeURIComponent(runId)}`);
17794
+ if (!response.ok)
17795
+ return null;
17796
+ return normalizeRemoteKnowledgeRunContract(await response.json(), { id: runId, type: "status" });
17797
+ }
17798
+ async runLogs(runId) {
17799
+ const response = await this.request(`/api/v1/knowledge/runs/${encodeURIComponent(runId)}/logs`);
17800
+ if (!response.ok)
17801
+ return [];
17802
+ const payload = await response.json();
17803
+ return Array.isArray(payload) ? payload : [];
17804
+ }
17805
+ async runArtifacts(runId) {
17806
+ const response = await this.request(`/api/v1/knowledge/runs/${encodeURIComponent(runId)}/artifacts`);
17807
+ if (!response.ok)
17808
+ return [];
17809
+ const payload = await response.json();
17810
+ return Array.isArray(payload) ? payload : [];
17811
+ }
17812
+ }
17813
+
17563
17814
  // src/web-search.ts
17564
17815
  import { createHash as createHash8, randomUUID as randomUUID7 } from "crypto";
17565
17816
  function stableHash(value) {
@@ -17895,6 +18146,15 @@ function resolveStorageContract(config2, workspace, scope = "global") {
17895
18146
  kms_key_configured: Boolean(s3.kms_key_id)
17896
18147
  } : null
17897
18148
  },
18149
+ hosted: {
18150
+ enabled: config2.mode === "hosted",
18151
+ api_url: normalizeKnowledgeApiOrigin(config2.hosted?.api_url ?? DEFAULT_KNOWLEDGE_API_URL),
18152
+ api_url_env: "KNOWLEDGE_API_URL",
18153
+ api_key_env: "KNOWLEDGE_API_KEY",
18154
+ auth_storage: "~/.hasna/knowledge/auth.json",
18155
+ remote_contract_version: REMOTE_KNOWLEDGE_CONTRACT_VERSION,
18156
+ requires_hosted_account_for_local_use: false
18157
+ },
17898
18158
  source_ownership: {
17899
18159
  owner: "open-files",
17900
18160
  preferred_ref: config2.sources.preferred_ref,
@@ -17950,6 +18210,13 @@ function validateStorageConfig(config2, workspace) {
17950
18210
  if (!config2.sources.allowed_schemes.includes("open-files")) {
17951
18211
  errors3.push("sources.allowed_schemes must include open-files.");
17952
18212
  }
18213
+ if (config2.mode === "hosted" && config2.hosted?.api_url) {
18214
+ try {
18215
+ normalizeKnowledgeApiOrigin(config2.hosted.api_url);
18216
+ } catch {
18217
+ errors3.push("hosted.api_url must be an http(s) URL when mode is hosted.");
18218
+ }
18219
+ }
17953
18220
  return {
17954
18221
  ok: errors3.length === 0,
17955
18222
  errors: errors3,
@@ -18188,6 +18455,17 @@ function recordWikiLayoutCatalog(db, artifacts, now = new Date) {
18188
18455
  }
18189
18456
 
18190
18457
  // src/service.ts
18458
+ function normalizeMode(value) {
18459
+ if (!value)
18460
+ return;
18461
+ const normalized = value.trim().toLowerCase();
18462
+ if (normalized === "local" || normalized === "offline")
18463
+ return "local";
18464
+ if (normalized === "hosted" || normalized === "remote" || normalized === "knowledge.hasna.xyz")
18465
+ return "hosted";
18466
+ throw new Error("Invalid setup mode. Use hosted or local.");
18467
+ }
18468
+
18191
18469
  class KnowledgeService {
18192
18470
  options;
18193
18471
  ensuredWorkspace;
@@ -18228,6 +18506,59 @@ class KnowledgeService {
18228
18506
  validateStorage() {
18229
18507
  return validateStorageConfig(this.config(), this.ensureWorkspace());
18230
18508
  }
18509
+ setup(options = {}) {
18510
+ const workspace = this.ensureWorkspace();
18511
+ const current = this.config();
18512
+ const mode = normalizeMode(options.mode) ?? current.mode;
18513
+ const apiUrl = options.apiUrl ? normalizeKnowledgeApiOrigin(options.apiUrl) : current.hosted?.api_url ? normalizeKnowledgeApiOrigin(current.hosted.api_url) : null;
18514
+ const nextConfig = {
18515
+ ...current,
18516
+ mode,
18517
+ hosted: {
18518
+ ...current.hosted ?? {},
18519
+ ...apiUrl ? { api_url: apiUrl } : {}
18520
+ }
18521
+ };
18522
+ writeKnowledgeConfig(workspace.configPath, nextConfig);
18523
+ this.cachedConfig = nextConfig;
18524
+ return {
18525
+ ok: true,
18526
+ mode,
18527
+ api_url: nextConfig.hosted?.api_url ?? null,
18528
+ config_path: workspace.configPath,
18529
+ next: mode === "hosted" ? ["open-knowledge auth login --api-key <key>", "open-knowledge remote contracts --json"] : ["open-knowledge search <query>", "knowledge <prompt>"],
18530
+ message: `Set knowledge mode to ${mode}`
18531
+ };
18532
+ }
18533
+ authStatus(env = process.env) {
18534
+ return knowledgeAuthStatus(this.config(), env);
18535
+ }
18536
+ saveAuth(input, env = process.env) {
18537
+ const apiUrl = input.apiUrl ?? this.config().hosted?.api_url;
18538
+ return saveKnowledgeAuth({
18539
+ api_key: input.apiKey,
18540
+ email: input.email,
18541
+ org_id: input.orgId,
18542
+ org_slug: input.orgSlug,
18543
+ user_id: input.userId,
18544
+ api_url: apiUrl
18545
+ }, env);
18546
+ }
18547
+ clearAuth(env = process.env) {
18548
+ return clearKnowledgeAuth(env);
18549
+ }
18550
+ remoteContract() {
18551
+ const storage = this.storageContract();
18552
+ return knowledgeRegistryContract({
18553
+ mode: this.config().mode,
18554
+ sourceSchemes: this.config().sources.allowed_schemes,
18555
+ storageType: storage.artifact_store.type,
18556
+ artifactUriPrefix: storage.artifact_store.uri_prefix
18557
+ });
18558
+ }
18559
+ remoteClient(env = process.env) {
18560
+ return RemoteKnowledgeClient.fromConfig(this.config(), env);
18561
+ }
18231
18562
  paths() {
18232
18563
  const workspace = this.ensureWorkspace();
18233
18564
  return {
@@ -18970,7 +19301,7 @@ function buildServer() {
18970
19301
  const storePath = resolveStorePath(store_path, scope);
18971
19302
  return readStoreLocked(storePath, (db) => {
18972
19303
  const filePath = file2 || "./knowledge-export.json";
18973
- writeFileSync4(filePath, JSON.stringify(db, null, 2));
19304
+ writeFileSync5(filePath, JSON.stringify(db, null, 2));
18974
19305
  return jsonText({ ok: true, file: filePath, count: db.items.length });
18975
19306
  });
18976
19307
  });
@@ -18979,9 +19310,9 @@ function buildServer() {
18979
19310
  store_path: storePathField,
18980
19311
  scope: scopeField
18981
19312
  }, async ({ file: file2, store_path, scope }) => {
18982
- if (!existsSync7(file2))
19313
+ if (!existsSync8(file2))
18983
19314
  return errorText(`File not found: ${file2}`);
18984
- const imported = JSON.parse(readFileSync7(file2, "utf8"));
19315
+ const imported = JSON.parse(readFileSync8(file2, "utf8"));
18985
19316
  if (!imported || !Array.isArray(imported.items))
18986
19317
  return errorText('Invalid import file: expected {"items": [...]}');
18987
19318
  const storePath = resolveStorePath(store_path, scope);