@lobu/cli 4.3.0 → 6.0.0

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.
Files changed (266) hide show
  1. package/README.md +11 -26
  2. package/dist/__tests__/chat.integration.test.js +19 -19
  3. package/dist/__tests__/chat.integration.test.js.map +1 -1
  4. package/dist/__tests__/dev.test.d.ts +2 -0
  5. package/dist/__tests__/dev.test.d.ts.map +1 -0
  6. package/dist/__tests__/dev.test.js +25 -0
  7. package/dist/__tests__/dev.test.js.map +1 -0
  8. package/dist/__tests__/init-memory.test.js +1 -14
  9. package/dist/__tests__/init-memory.test.js.map +1 -1
  10. package/dist/__tests__/token.test.d.ts +2 -0
  11. package/dist/__tests__/token.test.d.ts.map +1 -0
  12. package/dist/__tests__/token.test.js +52 -0
  13. package/dist/__tests__/token.test.js.map +1 -0
  14. package/dist/bundled-skills/lobu/SKILL.md +95 -7
  15. package/dist/commands/_lib/apply/__tests__/client.test.d.ts +2 -0
  16. package/dist/commands/_lib/apply/__tests__/client.test.d.ts.map +1 -0
  17. package/dist/commands/_lib/apply/__tests__/client.test.js +23 -0
  18. package/dist/commands/_lib/apply/__tests__/client.test.js.map +1 -0
  19. package/dist/commands/_lib/apply/__tests__/desired-state.test.d.ts +2 -0
  20. package/dist/commands/_lib/apply/__tests__/desired-state.test.d.ts.map +1 -0
  21. package/dist/commands/_lib/apply/__tests__/desired-state.test.js +140 -0
  22. package/dist/commands/_lib/apply/__tests__/desired-state.test.js.map +1 -0
  23. package/dist/commands/_lib/apply/__tests__/diff.test.d.ts +2 -0
  24. package/dist/commands/_lib/apply/__tests__/diff.test.d.ts.map +1 -0
  25. package/dist/commands/_lib/apply/__tests__/diff.test.js +378 -0
  26. package/dist/commands/_lib/apply/__tests__/diff.test.js.map +1 -0
  27. package/dist/commands/_lib/apply/apply-cmd.d.ts +12 -0
  28. package/dist/commands/_lib/apply/apply-cmd.d.ts.map +1 -0
  29. package/dist/commands/_lib/apply/apply-cmd.js +183 -0
  30. package/dist/commands/_lib/apply/apply-cmd.js.map +1 -0
  31. package/dist/commands/_lib/apply/client.d.ts +130 -0
  32. package/dist/commands/_lib/apply/client.d.ts.map +1 -0
  33. package/dist/commands/_lib/apply/client.js +243 -0
  34. package/dist/commands/_lib/apply/client.js.map +1 -0
  35. package/dist/commands/_lib/apply/desired-state.d.ts +71 -0
  36. package/dist/commands/_lib/apply/desired-state.d.ts.map +1 -0
  37. package/dist/commands/_lib/apply/desired-state.js +600 -0
  38. package/dist/commands/_lib/apply/desired-state.js.map +1 -0
  39. package/dist/commands/_lib/apply/diff.d.ts +76 -0
  40. package/dist/commands/_lib/apply/diff.d.ts.map +1 -0
  41. package/dist/commands/_lib/apply/diff.js +323 -0
  42. package/dist/commands/_lib/apply/diff.js.map +1 -0
  43. package/dist/commands/_lib/apply/prompt.d.ts +13 -0
  44. package/dist/commands/_lib/apply/prompt.d.ts.map +1 -0
  45. package/dist/commands/_lib/apply/prompt.js +19 -0
  46. package/dist/commands/_lib/apply/prompt.js.map +1 -0
  47. package/dist/commands/_lib/apply/render.d.ts +9 -0
  48. package/dist/commands/_lib/apply/render.d.ts.map +1 -0
  49. package/dist/commands/_lib/apply/render.js +92 -0
  50. package/dist/commands/_lib/apply/render.js.map +1 -0
  51. package/dist/commands/agent.d.ts +27 -0
  52. package/dist/commands/agent.d.ts.map +1 -0
  53. package/dist/commands/agent.js +111 -0
  54. package/dist/commands/agent.js.map +1 -0
  55. package/dist/commands/apply.d.ts +3 -0
  56. package/dist/commands/apply.d.ts.map +1 -0
  57. package/dist/commands/apply.js +5 -0
  58. package/dist/commands/apply.js.map +1 -0
  59. package/dist/commands/chat.d.ts.map +1 -1
  60. package/dist/commands/chat.js +5 -5
  61. package/dist/commands/chat.js.map +1 -1
  62. package/dist/commands/dev.d.ts +8 -3
  63. package/dist/commands/dev.d.ts.map +1 -1
  64. package/dist/commands/dev.js +128 -124
  65. package/dist/commands/dev.js.map +1 -1
  66. package/dist/commands/doctor.d.ts +6 -0
  67. package/dist/commands/doctor.d.ts.map +1 -0
  68. package/dist/commands/doctor.js +75 -0
  69. package/dist/commands/doctor.js.map +1 -0
  70. package/dist/commands/eval.d.ts +1 -0
  71. package/dist/commands/eval.d.ts.map +1 -1
  72. package/dist/commands/eval.js +7 -4
  73. package/dist/commands/eval.js.map +1 -1
  74. package/dist/commands/init.d.ts +2 -9
  75. package/dist/commands/init.d.ts.map +1 -1
  76. package/dist/commands/init.js +35 -249
  77. package/dist/commands/init.js.map +1 -1
  78. package/dist/commands/login.d.ts +11 -3
  79. package/dist/commands/login.d.ts.map +1 -1
  80. package/dist/commands/login.js +109 -251
  81. package/dist/commands/login.js.map +1 -1
  82. package/dist/commands/logout.d.ts.map +1 -1
  83. package/dist/commands/logout.js +12 -11
  84. package/dist/commands/logout.js.map +1 -1
  85. package/dist/commands/memory/_lib/agent-detect.d.ts +9 -0
  86. package/dist/commands/memory/_lib/agent-detect.d.ts.map +1 -0
  87. package/dist/commands/memory/_lib/agent-detect.js +82 -0
  88. package/dist/commands/memory/_lib/agent-detect.js.map +1 -0
  89. package/dist/commands/memory/_lib/browser-auth-cmd.d.ts +19 -0
  90. package/dist/commands/memory/_lib/browser-auth-cmd.d.ts.map +1 -0
  91. package/dist/commands/memory/_lib/browser-auth-cmd.js +635 -0
  92. package/dist/commands/memory/_lib/browser-auth-cmd.js.map +1 -0
  93. package/dist/commands/memory/_lib/browser.d.ts +2 -0
  94. package/dist/commands/memory/_lib/browser.d.ts.map +1 -0
  95. package/dist/commands/memory/_lib/browser.js +26 -0
  96. package/dist/commands/memory/_lib/browser.js.map +1 -0
  97. package/dist/commands/memory/_lib/errors.d.ts +13 -0
  98. package/dist/commands/memory/_lib/errors.d.ts.map +1 -0
  99. package/dist/commands/memory/_lib/errors.js +23 -0
  100. package/dist/commands/memory/_lib/errors.js.map +1 -0
  101. package/dist/commands/memory/_lib/init-wizard.d.ts +12 -0
  102. package/dist/commands/memory/_lib/init-wizard.d.ts.map +1 -0
  103. package/dist/commands/memory/_lib/init-wizard.js +182 -0
  104. package/dist/commands/memory/_lib/init-wizard.js.map +1 -0
  105. package/dist/commands/memory/_lib/install-targets.d.ts +15 -0
  106. package/dist/commands/memory/_lib/install-targets.d.ts.map +1 -0
  107. package/dist/commands/memory/_lib/install-targets.js +209 -0
  108. package/dist/commands/memory/_lib/install-targets.js.map +1 -0
  109. package/dist/commands/memory/_lib/mcp.d.ts +20 -0
  110. package/dist/commands/memory/_lib/mcp.d.ts.map +1 -0
  111. package/dist/commands/memory/_lib/mcp.js +182 -0
  112. package/dist/commands/memory/_lib/mcp.js.map +1 -0
  113. package/dist/commands/memory/_lib/openclaw-auth.d.ts +41 -0
  114. package/dist/commands/memory/_lib/openclaw-auth.d.ts.map +1 -0
  115. package/dist/commands/memory/_lib/openclaw-auth.js +133 -0
  116. package/dist/commands/memory/_lib/openclaw-auth.js.map +1 -0
  117. package/dist/commands/memory/_lib/openclaw-auth.test.d.ts +2 -0
  118. package/dist/commands/memory/_lib/openclaw-auth.test.d.ts.map +1 -0
  119. package/dist/commands/memory/_lib/openclaw-auth.test.js +9 -0
  120. package/dist/commands/memory/_lib/openclaw-auth.test.js.map +1 -0
  121. package/dist/commands/memory/_lib/openclaw-cmd.d.ts +15 -0
  122. package/dist/commands/memory/_lib/openclaw-cmd.d.ts.map +1 -0
  123. package/dist/commands/memory/_lib/openclaw-cmd.js +208 -0
  124. package/dist/commands/memory/_lib/openclaw-cmd.js.map +1 -0
  125. package/dist/commands/memory/_lib/output.d.ts +5 -0
  126. package/dist/commands/memory/_lib/output.d.ts.map +1 -0
  127. package/dist/commands/memory/_lib/output.js +17 -0
  128. package/dist/commands/memory/_lib/output.js.map +1 -0
  129. package/dist/commands/memory/_lib/schema.d.ts +93 -0
  130. package/dist/commands/memory/_lib/schema.d.ts.map +1 -0
  131. package/dist/commands/memory/_lib/schema.js +192 -0
  132. package/dist/commands/memory/_lib/schema.js.map +1 -0
  133. package/dist/commands/memory/_lib/seed-cmd.d.ts +9 -0
  134. package/dist/commands/memory/_lib/seed-cmd.d.ts.map +1 -0
  135. package/dist/commands/memory/_lib/seed-cmd.js +516 -0
  136. package/dist/commands/memory/_lib/seed-cmd.js.map +1 -0
  137. package/dist/commands/memory/browser-auth.d.ts +3 -0
  138. package/dist/commands/memory/browser-auth.d.ts.map +1 -0
  139. package/dist/commands/memory/browser-auth.js +5 -0
  140. package/dist/commands/memory/browser-auth.js.map +1 -0
  141. package/dist/commands/memory/configure.d.ts +3 -0
  142. package/dist/commands/memory/configure.d.ts.map +1 -0
  143. package/dist/commands/memory/configure.js +5 -0
  144. package/dist/commands/memory/configure.js.map +1 -0
  145. package/dist/commands/memory/health.d.ts +3 -0
  146. package/dist/commands/memory/health.d.ts.map +1 -0
  147. package/dist/commands/memory/health.js +5 -0
  148. package/dist/commands/memory/health.js.map +1 -0
  149. package/dist/commands/memory/init.d.ts +8 -0
  150. package/dist/commands/memory/init.d.ts.map +1 -0
  151. package/dist/commands/memory/init.js +65 -0
  152. package/dist/commands/memory/init.js.map +1 -0
  153. package/dist/commands/memory/org.d.ts +7 -0
  154. package/dist/commands/memory/org.d.ts.map +1 -0
  155. package/dist/commands/memory/org.js +22 -0
  156. package/dist/commands/memory/org.js.map +1 -0
  157. package/dist/commands/memory/run.d.ts +8 -0
  158. package/dist/commands/memory/run.d.ts.map +1 -0
  159. package/dist/commands/memory/run.js +58 -0
  160. package/dist/commands/memory/run.js.map +1 -0
  161. package/dist/commands/memory/seed.d.ts +3 -0
  162. package/dist/commands/memory/seed.d.ts.map +1 -0
  163. package/dist/commands/memory/seed.js +5 -0
  164. package/dist/commands/memory/seed.js.map +1 -0
  165. package/dist/commands/org.d.ts +10 -0
  166. package/dist/commands/org.d.ts.map +1 -0
  167. package/dist/commands/org.js +34 -0
  168. package/dist/commands/org.js.map +1 -0
  169. package/dist/commands/{connections/platforms.d.ts → platforms/platform-prompts.d.ts} +5 -5
  170. package/dist/commands/platforms/platform-prompts.d.ts.map +1 -0
  171. package/dist/commands/{connections/platforms.js → platforms/platform-prompts.js} +24 -24
  172. package/dist/commands/platforms/platform-prompts.js.map +1 -0
  173. package/dist/commands/status.d.ts +4 -1
  174. package/dist/commands/status.d.ts.map +1 -1
  175. package/dist/commands/status.js +15 -84
  176. package/dist/commands/status.js.map +1 -1
  177. package/dist/commands/token.d.ts +17 -0
  178. package/dist/commands/token.d.ts.map +1 -0
  179. package/dist/commands/token.js +55 -0
  180. package/dist/commands/token.js.map +1 -0
  181. package/dist/index.d.ts.map +1 -1
  182. package/dist/index.js +303 -88
  183. package/dist/index.js.map +1 -1
  184. package/dist/internal/__tests__/api-client.test.d.ts +2 -0
  185. package/dist/internal/__tests__/api-client.test.d.ts.map +1 -0
  186. package/dist/internal/__tests__/api-client.test.js +95 -0
  187. package/dist/internal/__tests__/api-client.test.js.map +1 -0
  188. package/dist/internal/__tests__/context.test.d.ts +2 -0
  189. package/dist/internal/__tests__/context.test.d.ts.map +1 -0
  190. package/dist/internal/__tests__/context.test.js +77 -0
  191. package/dist/internal/__tests__/context.test.js.map +1 -0
  192. package/dist/internal/api-client.d.ts +39 -0
  193. package/dist/internal/api-client.d.ts.map +1 -0
  194. package/dist/internal/api-client.js +217 -0
  195. package/dist/internal/api-client.js.map +1 -0
  196. package/dist/internal/context.d.ts +10 -1
  197. package/dist/internal/context.d.ts.map +1 -1
  198. package/dist/internal/context.js +109 -2
  199. package/dist/internal/context.js.map +1 -1
  200. package/dist/internal/credentials.d.ts +12 -2
  201. package/dist/internal/credentials.d.ts.map +1 -1
  202. package/dist/internal/credentials.js +47 -54
  203. package/dist/internal/credentials.js.map +1 -1
  204. package/dist/internal/gateway-url.d.ts +4 -4
  205. package/dist/internal/gateway-url.d.ts.map +1 -1
  206. package/dist/internal/gateway-url.js +6 -5
  207. package/dist/internal/gateway-url.js.map +1 -1
  208. package/dist/internal/index.d.ts +5 -3
  209. package/dist/internal/index.d.ts.map +1 -1
  210. package/dist/internal/index.js +3 -2
  211. package/dist/internal/index.js.map +1 -1
  212. package/dist/internal/local-env.d.ts +2 -0
  213. package/dist/internal/local-env.d.ts.map +1 -0
  214. package/dist/internal/local-env.js +32 -0
  215. package/dist/internal/local-env.js.map +1 -0
  216. package/dist/internal/oauth.d.ts +82 -0
  217. package/dist/internal/oauth.d.ts.map +1 -0
  218. package/dist/internal/oauth.js +234 -0
  219. package/dist/internal/oauth.js.map +1 -0
  220. package/dist/server.bundle.mjs +72219 -0
  221. package/dist/templates/.env.tmpl +7 -3
  222. package/dist/templates/README.md.tmpl +20 -53
  223. package/package.json +60 -6
  224. package/dist/__tests__/skills.test.d.ts +0 -2
  225. package/dist/__tests__/skills.test.d.ts.map +0 -1
  226. package/dist/__tests__/skills.test.js +0 -33
  227. package/dist/__tests__/skills.test.js.map +0 -1
  228. package/dist/commands/connections/add.d.ts +0 -2
  229. package/dist/commands/connections/add.d.ts.map +0 -1
  230. package/dist/commands/connections/add.js +0 -46
  231. package/dist/commands/connections/add.js.map +0 -1
  232. package/dist/commands/connections/list.d.ts +0 -2
  233. package/dist/commands/connections/list.d.ts.map +0 -1
  234. package/dist/commands/connections/list.js +0 -25
  235. package/dist/commands/connections/list.js.map +0 -1
  236. package/dist/commands/connections/platforms.d.ts.map +0 -1
  237. package/dist/commands/connections/platforms.js.map +0 -1
  238. package/dist/commands/providers/add.d.ts +0 -2
  239. package/dist/commands/providers/add.d.ts.map +0 -1
  240. package/dist/commands/providers/add.js +0 -50
  241. package/dist/commands/providers/add.js.map +0 -1
  242. package/dist/commands/providers/list.d.ts +0 -2
  243. package/dist/commands/providers/list.d.ts.map +0 -1
  244. package/dist/commands/providers/list.js +0 -22
  245. package/dist/commands/providers/list.js.map +0 -1
  246. package/dist/commands/secrets.d.ts +0 -8
  247. package/dist/commands/secrets.d.ts.map +0 -1
  248. package/dist/commands/secrets.js +0 -98
  249. package/dist/commands/secrets.js.map +0 -1
  250. package/dist/commands/skills/add.d.ts +0 -5
  251. package/dist/commands/skills/add.d.ts.map +0 -1
  252. package/dist/commands/skills/add.js +0 -27
  253. package/dist/commands/skills/add.js.map +0 -1
  254. package/dist/commands/skills/list.d.ts +0 -2
  255. package/dist/commands/skills/list.d.ts.map +0 -1
  256. package/dist/commands/skills/list.js +0 -19
  257. package/dist/commands/skills/list.js.map +0 -1
  258. package/dist/commands/skills/registry.d.ts +0 -19
  259. package/dist/commands/skills/registry.d.ts.map +0 -1
  260. package/dist/commands/skills/registry.js +0 -76
  261. package/dist/commands/skills/registry.js.map +0 -1
  262. package/dist/config/agent-helpers.d.ts +0 -38
  263. package/dist/config/agent-helpers.d.ts.map +0 -1
  264. package/dist/config/agent-helpers.js +0 -58
  265. package/dist/config/agent-helpers.js.map +0 -1
  266. package/dist/templates/Dockerfile.worker.tmpl +0 -29
@@ -0,0 +1,95 @@
1
+ import { afterEach, beforeEach, describe, expect, mock, spyOn, test, } from "bun:test";
2
+ import { ApiClient, listOrganizations, resolveApiClient } from "../api-client";
3
+ import * as context from "../context";
4
+ import * as credentials from "../credentials";
5
+ describe("ApiClient", () => {
6
+ test("request sends correct headers", async () => {
7
+ const fetchMock = mock(async () => {
8
+ return new Response(JSON.stringify({ ok: true }), { status: 200 });
9
+ });
10
+ const client = new ApiClient("https://api.example.com", "my-token", fetchMock);
11
+ const result = await client.get("/test");
12
+ expect(result).toEqual({ ok: true });
13
+ const [url, init] = fetchMock.mock.calls[0];
14
+ expect(url).toBe("https://api.example.com/test");
15
+ expect(init.headers).toMatchObject({
16
+ Authorization: "Bearer my-token",
17
+ Accept: "application/json",
18
+ });
19
+ });
20
+ test("request throws ApiClientError on failure", async () => {
21
+ const fetchMock = mock(async () => {
22
+ return new Response(JSON.stringify({ error: "Failed", message: "Error message" }), { status: 400 });
23
+ });
24
+ const client = new ApiClient("https://api.example.com", "my-token", fetchMock);
25
+ expect(client.get("/fail")).rejects.toThrow("GET /fail failed: Error message");
26
+ });
27
+ });
28
+ describe("resolveApiClient", () => {
29
+ beforeEach(() => {
30
+ delete process.env.LOBU_API_TOKEN;
31
+ delete process.env.LOBU_ORG;
32
+ });
33
+ afterEach(() => {
34
+ mock.restore();
35
+ });
36
+ test("resolves the token from the context that owns an overridden API URL", async () => {
37
+ spyOn(context, "resolveContext").mockResolvedValue({
38
+ name: "default",
39
+ apiUrl: "https://app.lobu.ai/api/v1",
40
+ source: "default",
41
+ });
42
+ spyOn(context, "findContextByUrl").mockImplementation(async (url) => {
43
+ if (url === "https://custom.lobu.ai/api/v1") {
44
+ return {
45
+ name: "custom",
46
+ apiUrl: "https://custom.lobu.ai/api/v1",
47
+ source: "config",
48
+ };
49
+ }
50
+ return undefined;
51
+ });
52
+ spyOn(credentials, "getToken").mockImplementation(async (name) => {
53
+ if (name === "custom")
54
+ return "custom-token";
55
+ if (name === "default")
56
+ return "default-token";
57
+ return null;
58
+ });
59
+ spyOn(context, "getActiveOrg").mockResolvedValue("my-org");
60
+ const resolved = await resolveApiClient({
61
+ apiUrl: "https://custom.lobu.ai/api/v1",
62
+ });
63
+ expect(resolved.contextName).toBe("custom");
64
+ expect(resolved.token).toBe("custom-token");
65
+ expect(resolved.apiBaseUrl).toBe("https://custom.lobu.ai");
66
+ await expect(resolveApiClient({ apiUrl: "https://unknown.lobu.ai/api/v1" })).rejects.toThrow("Refusing to send stored context credentials");
67
+ });
68
+ test("reads the active org from the resolved context", async () => {
69
+ spyOn(context, "resolveContext").mockResolvedValue({
70
+ name: "prod",
71
+ apiUrl: "https://app.lobu.ai/api/v1",
72
+ source: "config",
73
+ });
74
+ spyOn(credentials, "getToken").mockResolvedValue("prod-token");
75
+ const getActiveOrgSpy = spyOn(context, "getActiveOrg").mockImplementation(async (ctx) => {
76
+ if (ctx === "prod")
77
+ return "prod-org";
78
+ return "default-org";
79
+ });
80
+ const resolved = await resolveApiClient({ context: "prod" });
81
+ expect(resolved.orgSlug).toBe("prod-org");
82
+ expect(getActiveOrgSpy).toHaveBeenCalledWith("prod");
83
+ });
84
+ test("listOrganizations refuses unmatched URL overrides with stored credentials", async () => {
85
+ spyOn(context, "resolveContext").mockResolvedValue({
86
+ name: "default",
87
+ apiUrl: "https://app.lobu.ai/api/v1",
88
+ source: "default",
89
+ });
90
+ spyOn(context, "findContextByUrl").mockResolvedValue(undefined);
91
+ spyOn(credentials, "getToken").mockResolvedValue("default-token");
92
+ await expect(listOrganizations({ apiUrl: "https://unknown.lobu.ai/api/v1" })).rejects.toThrow("Refusing to send stored context credentials");
93
+ });
94
+ });
95
+ //# sourceMappingURL=api-client.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.test.js","sourceRoot":"","sources":["../../../src/internal/__tests__/api-client.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=context.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.test.d.ts","sourceRoot":"","sources":["../../../src/internal/__tests__/context.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,77 @@
1
+ import { afterEach, beforeEach, describe, expect, mock, spyOn, test, } from "bun:test";
2
+ import * as fs from "node:fs/promises";
3
+ import { DEFAULT_CONTEXT_NAME, findContextByMemoryUrl, findContextByUrl, getActiveOrg, loadContextConfig, setActiveOrg, } from "../context";
4
+ describe("context management", () => {
5
+ let readFileSpy;
6
+ let writeFileSpy;
7
+ beforeEach(() => {
8
+ delete process.env.LOBU_CONTEXT;
9
+ delete process.env.LOBU_ORG;
10
+ delete process.env.LOBU_API_URL;
11
+ delete process.env.LOBU_MEMORY_URL;
12
+ readFileSpy = spyOn(fs, "readFile");
13
+ writeFileSpy = spyOn(fs, "writeFile").mockResolvedValue(undefined);
14
+ spyOn(fs, "mkdir").mockResolvedValue(undefined);
15
+ });
16
+ afterEach(() => {
17
+ mock.restore();
18
+ });
19
+ test("loadContextConfig handles missing file", async () => {
20
+ readFileSpy.mockRejectedValue(new Error("File not found"));
21
+ const config = await loadContextConfig();
22
+ expect(config.currentContext).toBe(DEFAULT_CONTEXT_NAME);
23
+ expect(config.contexts[DEFAULT_CONTEXT_NAME]).toBeDefined();
24
+ });
25
+ test("stores and reads the active org per context", async () => {
26
+ const configData = {
27
+ currentContext: "prod",
28
+ contexts: {
29
+ lobu: {
30
+ apiUrl: "https://app.lobu.ai/api/v1",
31
+ activeOrg: "default-org",
32
+ },
33
+ prod: { apiUrl: "https://prod.lobu.ai/api/v1", activeOrg: "prod-org" },
34
+ },
35
+ };
36
+ readFileSpy.mockResolvedValue(JSON.stringify(configData));
37
+ expect(await getActiveOrg("lobu")).toBe("default-org");
38
+ expect(await getActiveOrg("prod")).toBe("prod-org");
39
+ expect(await getActiveOrg()).toBe("prod-org");
40
+ await setActiveOrg("new-org", "lobu");
41
+ const [, written] = writeFileSpy.mock.calls[0];
42
+ const saved = JSON.parse(written);
43
+ expect(saved.contexts.lobu.activeOrg).toBe("new-org");
44
+ expect(saved.contexts.prod.activeOrg).toBe("prod-org");
45
+ });
46
+ test("finds contexts by normalized API URL", async () => {
47
+ const configData = {
48
+ currentContext: "lobu",
49
+ contexts: {
50
+ lobu: { apiUrl: "https://app.lobu.ai/api/v1" },
51
+ custom: { apiUrl: "https://custom.lobu.ai/api/v1" },
52
+ },
53
+ };
54
+ readFileSpy.mockResolvedValue(JSON.stringify(configData));
55
+ const matched = await findContextByUrl("https://custom.lobu.ai/api/v1/");
56
+ expect(matched?.name).toBe("custom");
57
+ expect(matched?.apiUrl).toBe("https://custom.lobu.ai/api/v1");
58
+ const none = await findContextByUrl("https://unknown.ai");
59
+ expect(none).toBeUndefined();
60
+ });
61
+ test("finds contexts by normalized memory URL", async () => {
62
+ const configData = {
63
+ currentContext: "lobu",
64
+ contexts: {
65
+ lobu: { apiUrl: "https://app.lobu.ai/api/v1" },
66
+ local: {
67
+ apiUrl: "http://localhost:8787/api/v1",
68
+ memoryUrl: "http://localhost:8787/mcp/acme",
69
+ },
70
+ },
71
+ };
72
+ readFileSpy.mockResolvedValue(JSON.stringify(configData));
73
+ const matched = await findContextByMemoryUrl("http://localhost:8787/mcp");
74
+ expect(matched?.name).toBe("local");
75
+ });
76
+ });
77
+ //# sourceMappingURL=context.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.test.js","sourceRoot":"","sources":["../../../src/internal/__tests__/context.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ export interface ApiClientOptions {
2
+ context?: string;
3
+ org?: string;
4
+ apiUrl?: string;
5
+ fetchImpl?: typeof fetch;
6
+ }
7
+ export interface ResolvedApiClient {
8
+ client: ApiClient;
9
+ contextName: string;
10
+ apiBaseUrl: string;
11
+ orgSlug: string;
12
+ token: string;
13
+ }
14
+ export interface OrganizationInfo {
15
+ slug: string;
16
+ name?: string;
17
+ }
18
+ export declare class ApiClientError extends Error {
19
+ readonly status?: number | undefined;
20
+ readonly code?: string | undefined;
21
+ constructor(message: string, status?: number | undefined, code?: string | undefined);
22
+ }
23
+ export declare class ApiClient {
24
+ private readonly apiBaseUrl;
25
+ private readonly token;
26
+ private readonly fetchImpl;
27
+ constructor(apiBaseUrl: string, token: string, fetchImpl?: typeof fetch);
28
+ request<T>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", path: string, body?: unknown, options?: {
29
+ okStatuses?: number[];
30
+ }): Promise<T>;
31
+ get<T>(path: string): Promise<T>;
32
+ post<T>(path: string, body?: unknown): Promise<T>;
33
+ patch<T>(path: string, body?: unknown): Promise<T>;
34
+ delete<T>(path: string): Promise<T>;
35
+ }
36
+ export declare function resolveApiClient(options?: ApiClientOptions): Promise<ResolvedApiClient>;
37
+ export declare function listOrganizations(options?: Pick<ApiClientOptions, "context" | "apiUrl" | "fetchImpl">): Promise<OrganizationInfo[]>;
38
+ export declare function apiBaseFromContextUrl(apiUrl: string): string;
39
+ //# sourceMappingURL=api-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/internal/api-client.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,SAAS,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,cAAe,SAAQ,KAAK;aAGrB,MAAM,CAAC,EAAE,MAAM;aACf,IAAI,CAAC,EAAE,MAAM;gBAF7B,OAAO,EAAE,MAAM,EACC,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,IAAI,CAAC,EAAE,MAAM,YAAA;CAKhC;AAED,qBAAa,SAAS;IAElB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAFT,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,OAAO,KAAa;IAG5C,OAAO,CAAC,CAAC,EACb,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,EACnD,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,GAAE;QAAE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;KAAO,GACtC,OAAO,CAAC,CAAC,CAAC;IA0Bb,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAIhC,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIjD,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIlD,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;CAKpC;AAED,wBAAsB,gBAAgB,CACpC,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,iBAAiB,CAAC,CA2B5B;AAED,wBAAsB,iBAAiB,CACrC,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAM,GACvE,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAgB7B;AAkCD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAM5D"}
@@ -0,0 +1,217 @@
1
+ import { findContextByUrl, getActiveOrg, resolveContext, } from "./context.js";
2
+ import { getToken, loadCredentials } from "./credentials.js";
3
+ export class ApiClientError extends Error {
4
+ status;
5
+ code;
6
+ constructor(message, status, code) {
7
+ super(message);
8
+ this.status = status;
9
+ this.code = code;
10
+ this.name = "ApiClientError";
11
+ }
12
+ }
13
+ export class ApiClient {
14
+ apiBaseUrl;
15
+ token;
16
+ fetchImpl;
17
+ constructor(apiBaseUrl, token, fetchImpl = fetch) {
18
+ this.apiBaseUrl = apiBaseUrl;
19
+ this.token = token;
20
+ this.fetchImpl = fetchImpl;
21
+ }
22
+ async request(method, path, body, options = {}) {
23
+ const url = path.startsWith("http") ? path : `${this.apiBaseUrl}${path}`;
24
+ const headers = {
25
+ Accept: "application/json",
26
+ Authorization: `Bearer ${this.token}`,
27
+ };
28
+ const init = { method, headers };
29
+ if (body !== undefined) {
30
+ headers["Content-Type"] = "application/json";
31
+ init.body = JSON.stringify(body);
32
+ }
33
+ const response = await this.fetchImpl(url, init);
34
+ const parsed = await parseResponse(response, url);
35
+ const okStatuses = options.okStatuses ?? [200, 201, 204];
36
+ if (!response.ok || !okStatuses.includes(response.status)) {
37
+ const { message, code } = extractError(parsed, response);
38
+ throw new ApiClientError(`${method} ${path} failed: ${message}`, response.status, code);
39
+ }
40
+ return parsed;
41
+ }
42
+ get(path) {
43
+ return this.request("GET", path);
44
+ }
45
+ post(path, body) {
46
+ return this.request("POST", path, body);
47
+ }
48
+ patch(path, body) {
49
+ return this.request("PATCH", path, body);
50
+ }
51
+ delete(path) {
52
+ return this.request("DELETE", path, undefined, {
53
+ okStatuses: [200, 204],
54
+ });
55
+ }
56
+ }
57
+ export async function resolveApiClient(options = {}) {
58
+ const target = await resolveApiTarget(options);
59
+ const apiBaseUrl = apiBaseFromContextUrl(target.apiUrl);
60
+ const token = process.env.LOBU_API_TOKEN || (await getToken(target.name));
61
+ if (!token) {
62
+ throw new ApiClientError(`Not logged in to context "${target.name}". Run \`lobu login${options.context ? ` --context ${target.name}` : ""}\` first.`, 401);
63
+ }
64
+ const orgSlug = await resolveOrgSlug({
65
+ ...options,
66
+ contextName: target.name,
67
+ token,
68
+ apiBaseUrl,
69
+ useStoredUserInfoEndpoint: target.useStoredUserInfoEndpoint,
70
+ });
71
+ return {
72
+ client: new ApiClient(apiBaseUrl, token, options.fetchImpl),
73
+ contextName: target.name,
74
+ apiBaseUrl,
75
+ orgSlug,
76
+ token,
77
+ };
78
+ }
79
+ export async function listOrganizations(options = {}) {
80
+ const target = await resolveApiTarget(options);
81
+ const token = process.env.LOBU_API_TOKEN || (await getToken(target.name));
82
+ if (!token) {
83
+ throw new ApiClientError(`Not logged in to context "${target.name}". Run \`lobu login${options.context ? ` --context ${target.name}` : ""}\` first.`, 401);
84
+ }
85
+ return getOrganizationsFromUserInfo(target.name, token, apiBaseFromContextUrl(target.apiUrl), options.fetchImpl, { useStoredUserInfoEndpoint: target.useStoredUserInfoEndpoint });
86
+ }
87
+ async function resolveApiTarget(options) {
88
+ const requested = await resolveContext(options.context);
89
+ if (!options.apiUrl) {
90
+ return { ...requested, useStoredUserInfoEndpoint: true };
91
+ }
92
+ const matched = await findContextByUrl(options.apiUrl);
93
+ if (matched) {
94
+ return { ...matched, useStoredUserInfoEndpoint: true };
95
+ }
96
+ const apiBaseUrl = apiBaseFromContextUrl(options.apiUrl);
97
+ const contextApiBaseUrl = apiBaseFromContextUrl(requested.apiUrl);
98
+ if (!process.env.LOBU_API_TOKEN && apiBaseUrl !== contextApiBaseUrl) {
99
+ throw new ApiClientError(`Refusing to send stored context credentials for "${requested.name}" to ${apiBaseUrl}. Add a context for that URL or set LOBU_API_TOKEN explicitly.`);
100
+ }
101
+ return {
102
+ ...requested,
103
+ apiUrl: options.apiUrl,
104
+ useStoredUserInfoEndpoint: apiBaseUrl === contextApiBaseUrl,
105
+ };
106
+ }
107
+ export function apiBaseFromContextUrl(apiUrl) {
108
+ const url = new URL(apiUrl);
109
+ url.pathname = "";
110
+ url.search = "";
111
+ url.hash = "";
112
+ return url.toString().replace(/\/+$/, "");
113
+ }
114
+ async function resolveOrgSlug(options) {
115
+ const explicit = options.org?.trim() || process.env.LOBU_ORG?.trim();
116
+ if (explicit)
117
+ return validateOrgSlug(explicit);
118
+ const active = await getActiveOrg(options.contextName);
119
+ if (active)
120
+ return validateOrgSlug(active);
121
+ const organizations = await getOrganizationsFromUserInfo(options.contextName, options.token, options.apiBaseUrl, options.fetchImpl, { useStoredUserInfoEndpoint: options.useStoredUserInfoEndpoint }).catch(() => []);
122
+ if (organizations.length === 1) {
123
+ return validateOrgSlug(organizations[0].slug);
124
+ }
125
+ if (organizations.length > 1) {
126
+ throw new ApiClientError(`Multiple organizations are available (${organizations.map((org) => org.slug).join(", ")}). Run \`lobu org set <slug>\` or pass \`--org <slug>\`.`);
127
+ }
128
+ throw new ApiClientError("No organization selected. Run `lobu org set <slug>` or pass `--org <slug>`.");
129
+ }
130
+ async function getOrganizationsFromUserInfo(contextName, token, apiBaseUrl, fetchImpl = fetch, options = {}) {
131
+ const creds = options.useStoredUserInfoEndpoint === false
132
+ ? null
133
+ : await loadCredentials(contextName);
134
+ const endpoint = creds?.oauth?.userinfoEndpoint ?? `${apiBaseUrl}/oauth/userinfo`;
135
+ const response = await fetchImpl(endpoint, {
136
+ headers: {
137
+ Accept: "application/json",
138
+ Authorization: `Bearer ${token}`,
139
+ },
140
+ });
141
+ if (!response.ok)
142
+ return [];
143
+ const data = (await response.json().catch(() => null));
144
+ if (!data)
145
+ return [];
146
+ const orgs = Array.isArray(data.organizations) ? data.organizations : [];
147
+ const result = [];
148
+ for (const entry of orgs) {
149
+ if (!entry || typeof entry !== "object")
150
+ continue;
151
+ const value = entry;
152
+ const slug = typeof value.slug === "string" ? value.slug : "";
153
+ if (!slug)
154
+ continue;
155
+ result.push({
156
+ slug,
157
+ ...(typeof value.name === "string" ? { name: value.name } : {}),
158
+ });
159
+ }
160
+ return result;
161
+ }
162
+ async function parseResponse(response, url) {
163
+ if (response.status === 204)
164
+ return undefined;
165
+ const raw = await response.text();
166
+ if (!raw)
167
+ return undefined;
168
+ try {
169
+ return JSON.parse(raw);
170
+ }
171
+ catch {
172
+ if (!response.ok)
173
+ return { error: raw };
174
+ throw new ApiClientError(`Invalid JSON from ${url}: ${raw.slice(0, 500)}`, response.status);
175
+ }
176
+ }
177
+ function extractError(parsed, response) {
178
+ if (parsed && typeof parsed === "object") {
179
+ const record = parsed;
180
+ if (typeof record.error === "string") {
181
+ return {
182
+ message: pickString(record, "error_description") ??
183
+ pickString(record, "message") ??
184
+ record.error,
185
+ code: pickString(record, "code") ?? record.error,
186
+ };
187
+ }
188
+ if (record.error && typeof record.error === "object") {
189
+ const error = record.error;
190
+ return {
191
+ message: pickString(error, "message") ??
192
+ `HTTP ${response.status} ${response.statusText}`,
193
+ code: pickString(error, "code"),
194
+ };
195
+ }
196
+ if (typeof record.message === "string") {
197
+ return { message: record.message, code: pickString(record, "code") };
198
+ }
199
+ if (typeof record.error_description === "string") {
200
+ return {
201
+ message: record.error_description,
202
+ code: pickString(record, "error"),
203
+ };
204
+ }
205
+ }
206
+ return { message: `HTTP ${response.status} ${response.statusText}` };
207
+ }
208
+ function pickString(record, key) {
209
+ return typeof record[key] === "string" ? record[key] : undefined;
210
+ }
211
+ function validateOrgSlug(slug) {
212
+ if (!/^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?$/.test(slug)) {
213
+ throw new ApiClientError(`Invalid organization slug "${slug}". Slugs may only contain alphanumeric characters, hyphens, and underscores.`);
214
+ }
215
+ return slug;
216
+ }
217
+ //# sourceMappingURL=api-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../src/internal/api-client.ts"],"names":[],"mappings":"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"}
@@ -1,21 +1,30 @@
1
1
  export declare const LOBU_CONFIG_DIR: string;
2
2
  export declare const DEFAULT_CONTEXT_NAME = "lobu";
3
+ export declare const DEFAULT_MEMORY_URL = "https://lobu.ai/mcp";
3
4
  interface LobuContextEntry {
4
5
  apiUrl: string;
6
+ activeOrg?: string;
7
+ memoryUrl?: string;
5
8
  }
6
9
  interface LobuContextConfig {
7
10
  currentContext: string;
8
11
  contexts: Record<string, LobuContextEntry>;
9
12
  }
10
- interface ResolvedContext {
13
+ export interface ResolvedContext {
11
14
  name: string;
12
15
  apiUrl: string;
13
16
  source: "default" | "config" | "env";
14
17
  }
15
18
  export declare function loadContextConfig(): Promise<LobuContextConfig>;
16
19
  export declare function getCurrentContextName(): Promise<string>;
20
+ export declare function getActiveOrg(contextName?: string): Promise<string | undefined>;
21
+ export declare function getMemoryUrl(contextName?: string): Promise<string>;
22
+ export declare function setActiveOrg(orgSlug: string, contextName?: string): Promise<LobuContextConfig>;
23
+ export declare function setMemoryUrl(memoryUrl: string, contextName?: string): Promise<LobuContextConfig>;
17
24
  export declare function resolveContext(preferredContext?: string): Promise<ResolvedContext>;
18
25
  export declare function addContext(name: string, apiUrl: string): Promise<LobuContextConfig>;
19
26
  export declare function setCurrentContext(name: string): Promise<LobuContextConfig>;
27
+ export declare function findContextByUrl(apiUrl: string): Promise<ResolvedContext | undefined>;
28
+ export declare function findContextByMemoryUrl(memoryUrl: string): Promise<ResolvedContext | undefined>;
20
29
  export {};
21
30
  //# sourceMappingURL=context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/internal/context.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,eAAe,QAAqC,CAAC;AAClE,eAAO,MAAM,oBAAoB,SAAS,CAAC;AAK3C,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,iBAAiB;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CAC5C;AAED,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAC;CACtC;AAOD,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAQpE;AASD,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC,CAQ7D;AAED,wBAAsB,cAAc,CAClC,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,eAAe,CAAC,CA2B1B;AAED,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC,CAY5B;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iBAAiB,CAAC,CAgB5B"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/internal/context.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,eAAe,QAAqC,CAAC;AAClE,eAAO,MAAM,oBAAoB,SAAS,CAAC;AAK3C,eAAO,MAAM,kBAAkB,wBAAwB,CAAC;AAExD,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,iBAAiB;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAC;CACtC;AAOD,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAQpE;AASD,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC,CAQ7D;AAED,wBAAsB,YAAY,CAChC,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAO7B;AAED,wBAAsB,YAAY,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CASxE;AAED,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,iBAAiB,CAAC,CAqB5B;AAED,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,iBAAiB,CAAC,CAgB5B;AAED,wBAAsB,cAAc,CAClC,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,eAAe,CAAC,CA2B1B;AAED,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC,CAY5B;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iBAAiB,CAAC,CAgB5B;AA6DD,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAWtC;AAED,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CActC"}
@@ -5,6 +5,7 @@ export const LOBU_CONFIG_DIR = join(homedir(), ".config", "lobu");
5
5
  export const DEFAULT_CONTEXT_NAME = "lobu";
6
6
  const DEFAULT_API_URL = "https://app.lobu.ai/api/v1";
7
7
  const CONTEXTS_FILE = join(LOBU_CONFIG_DIR, "config.json");
8
+ export const DEFAULT_MEMORY_URL = "https://lobu.ai/mcp";
8
9
  export async function loadContextConfig() {
9
10
  try {
10
11
  const raw = await readFile(CONTEXTS_FILE, "utf-8");
@@ -29,6 +30,55 @@ export async function getCurrentContextName() {
29
30
  const config = await loadContextConfig();
30
31
  return config.currentContext;
31
32
  }
33
+ export async function getActiveOrg(contextName) {
34
+ const envOrg = process.env.LOBU_ORG?.trim();
35
+ if (envOrg)
36
+ return envOrg;
37
+ const config = await loadContextConfig();
38
+ const name = contextName || config.currentContext;
39
+ return config.contexts[name]?.activeOrg;
40
+ }
41
+ export async function getMemoryUrl(contextName) {
42
+ const envUrl = process.env.LOBU_MEMORY_URL?.trim();
43
+ if (envUrl)
44
+ return normalizeApiUrl(envUrl);
45
+ const config = await loadContextConfig();
46
+ const name = contextName || config.currentContext;
47
+ return normalizeApiUrl(config.contexts[name]?.memoryUrl || DEFAULT_MEMORY_URL);
48
+ }
49
+ export async function setActiveOrg(orgSlug, contextName) {
50
+ const trimmed = orgSlug.trim();
51
+ if (!trimmed) {
52
+ throw new Error("Organization slug cannot be empty.");
53
+ }
54
+ if (!/^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?$/.test(trimmed)) {
55
+ throw new Error(`Invalid organization slug "${orgSlug}". Slugs may only contain alphanumeric characters, hyphens, and underscores.`);
56
+ }
57
+ const config = await loadContextConfig();
58
+ const name = contextName || config.currentContext;
59
+ const context = config.contexts[name];
60
+ if (!context) {
61
+ throw new Error(`Unknown context "${name}".`);
62
+ }
63
+ context.activeOrg = trimmed;
64
+ await saveContextConfig(config);
65
+ return config;
66
+ }
67
+ export async function setMemoryUrl(memoryUrl, contextName) {
68
+ const trimmed = memoryUrl.trim();
69
+ if (!trimmed) {
70
+ throw new Error("Memory URL cannot be empty.");
71
+ }
72
+ const config = await loadContextConfig();
73
+ const name = contextName || config.currentContext;
74
+ const context = config.contexts[name];
75
+ if (!context) {
76
+ throw new Error(`Unknown context "${name}".`);
77
+ }
78
+ context.memoryUrl = normalizeAndValidateApiUrl(trimmed);
79
+ await saveContextConfig(config);
80
+ return config;
81
+ }
32
82
  export async function resolveContext(preferredContext) {
33
83
  const envApiUrl = process.env.LOBU_API_URL?.trim();
34
84
  const requestedContext = preferredContext?.trim() || process.env.LOBU_CONTEXT?.trim();
@@ -84,12 +134,23 @@ function normalizeContextConfig(raw) {
84
134
  if (!value || typeof value.apiUrl !== "string") {
85
135
  continue;
86
136
  }
87
- contexts[name] = { apiUrl: normalizeApiUrl(value.apiUrl) };
137
+ contexts[name] = {
138
+ apiUrl: normalizeApiUrl(value.apiUrl),
139
+ activeOrg: typeof value.activeOrg === "string"
140
+ ? value.activeOrg.trim()
141
+ : undefined,
142
+ memoryUrl: typeof value.memoryUrl === "string"
143
+ ? value.memoryUrl.trim()
144
+ : undefined,
145
+ };
88
146
  }
89
147
  const currentContext = raw.currentContext && contexts[raw.currentContext]
90
148
  ? raw.currentContext
91
149
  : DEFAULT_CONTEXT_NAME;
92
- return { currentContext, contexts };
150
+ return {
151
+ currentContext,
152
+ contexts,
153
+ };
93
154
  }
94
155
  function normalizeAndValidateApiUrl(apiUrl) {
95
156
  const normalized = normalizeApiUrl(apiUrl.trim());
@@ -114,4 +175,50 @@ function normalizeApiUrl(url) {
114
175
  }
115
176
  return end === url.length ? url : url.slice(0, end);
116
177
  }
178
+ export async function findContextByUrl(apiUrl) {
179
+ const config = await loadContextConfig();
180
+ const normalizedSearch = normalizeApiUrl(apiUrl);
181
+ for (const [name, context] of Object.entries(config.contexts)) {
182
+ if (normalizeApiUrl(context.apiUrl) === normalizedSearch) {
183
+ return contextToResolvedContext(name, context);
184
+ }
185
+ }
186
+ return undefined;
187
+ }
188
+ export async function findContextByMemoryUrl(memoryUrl) {
189
+ const config = await loadContextConfig();
190
+ const normalizedSearch = normalizeMemoryBaseUrl(memoryUrl);
191
+ for (const [name, context] of Object.entries(config.contexts)) {
192
+ const candidate = normalizeMemoryBaseUrl(context.memoryUrl || DEFAULT_MEMORY_URL);
193
+ if (candidate === normalizedSearch) {
194
+ return contextToResolvedContext(name, context);
195
+ }
196
+ }
197
+ return undefined;
198
+ }
199
+ function contextToResolvedContext(name, context) {
200
+ return {
201
+ name,
202
+ apiUrl: normalizeApiUrl(context.apiUrl),
203
+ source: name === DEFAULT_CONTEXT_NAME ? "default" : "config",
204
+ };
205
+ }
206
+ function normalizeMemoryBaseUrl(input) {
207
+ try {
208
+ const url = new URL(input);
209
+ url.hash = "";
210
+ url.search = "";
211
+ if (!url.pathname || url.pathname === "/") {
212
+ url.pathname = "/mcp";
213
+ }
214
+ else if (!url.pathname.startsWith("/mcp")) {
215
+ url.pathname = `${url.pathname.replace(/\/+$/, "")}/mcp`;
216
+ }
217
+ url.pathname = "/mcp";
218
+ return url.toString().replace(/\/+$/, "");
219
+ }
220
+ catch {
221
+ return normalizeApiUrl(input);
222
+ }
223
+ }
117
224
  //# sourceMappingURL=context.js.map