borgmcp 1.0.25 → 1.0.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.
@@ -6,15 +6,36 @@
6
6
  * CLIENT copy of the descriptor regex — byte-identical to the worker.
7
7
  */
8
8
  export declare const BACKEND_DESCRIPTOR_REGEX: RegExp;
9
+ /** Default Ollama endpoint when BORG_OLLAMA_BASE_URL is unset. */
10
+ export declare const OLLAMA_DEFAULT_BASE_URL = "http://localhost:11434";
11
+ /**
12
+ * Resolve the Ollama base URL: BORG_OLLAMA_BASE_URL override or the localhost
13
+ * default. The override is trimmed, defaulted to http:// when no scheme is
14
+ * given (Ollama serves plain http), and trailing slashes are stripped. Lets an
15
+ * Ollama drone point at a model server on another host, e.g.
16
+ * BORG_OLLAMA_BASE_URL=http://Mac-Studio.local:11434 borg assimilate ...
17
+ */
18
+ export declare function resolveOllamaBaseUrl(env?: Record<string, string | undefined>): string;
9
19
  export declare function parseBackend(descriptor: string): {
10
20
  kind: 'claude' | 'ollama';
11
21
  model: string;
12
22
  };
13
- export declare function resolveLaunchEnv(descriptor: string | null): {
23
+ export declare function resolveLaunchEnv(descriptor: string | null, ollamaBaseUrl?: string): {
14
24
  set: Record<string, string>;
15
25
  unset: string[];
16
26
  };
17
- export declare function checkBackendReachable(descriptor: string | null, fetchImpl: typeof fetch): Promise<{
27
+ /**
28
+ * Does `requested` match one of Ollama's pulled model names? Ollama defaults a
29
+ * tagless name to `:latest`, so `qwen3-coder-next` matches a pulled
30
+ * `qwen3-coder-next:latest`. Comparison is case-insensitive (Ollama lowercases
31
+ * model names). Tag presence is judged on the LAST path segment so a
32
+ * registry-host:port prefix colon (e.g. `localhost:5000/llama3`) is not mistaken
33
+ * for a tag. NOTE: digest refs (`name@sha256:…`) can't reach here — they're
34
+ * fenced out by BACKEND_DESCRIPTOR_REGEX (no `@`); widening that regex would
35
+ * require updating this matcher in lockstep.
36
+ */
37
+ export declare function ollamaModelMatches(requested: string, available: string[]): boolean;
38
+ export declare function checkBackendReachable(descriptor: string | null, fetchImpl: typeof fetch, ollamaBaseUrl?: string): Promise<{
18
39
  ok: boolean;
19
40
  message?: string;
20
41
  }>;
@@ -1 +1 @@
1
- const s=/^(claude|ollama):[A-Za-z0-9._:\/-]+$/;function o(e){const a=e.indexOf(":");if(a<0)throw new Error(`invalid backend descriptor: ${e} (expected <kind>:<model>)`);const t=e.substring(0,a),n=e.substring(a+1);if(t!=="claude"&&t!=="ollama")throw new Error(`invalid backend kind: ${t} (expected claude or ollama)`);return{kind:t,model:n}}function c(e){if(!e)return{set:{},unset:[]};const{kind:a,model:t}=o(e);return a==="claude"?{set:{ANTHROPIC_MODEL:t},unset:[]}:{set:{ANTHROPIC_BASE_URL:"http://localhost:11434",ANTHROPIC_MODEL:t,ANTHROPIC_AUTH_TOKEN:"ollama"},unset:["ANTHROPIC_API_KEY"]}}async function i(e,a){if(!e)return{ok:!0};const{kind:t}=o(e);if(t==="claude")return{ok:!0};const n=new AbortController,l=setTimeout(()=>n.abort(),3e3);try{return(await a("http://localhost:11434/api/tags",{signal:n.signal})).ok?{ok:!0}:{ok:!1,message:"Ollama not reachable at http://localhost:11434 \u2014 start it (ollama serve) or assimilate with a Claude backend."}}catch{return{ok:!1,message:"Ollama not reachable at http://localhost:11434 \u2014 start it (ollama serve) or assimilate with a Claude backend."}}finally{clearTimeout(l)}}export{s as BACKEND_DESCRIPTOR_REGEX,i as checkBackendReachable,o as parseBackend,c as resolveLaunchEnv};
1
+ const p=/^(claude|ollama):[A-Za-z0-9._:\/-]+$/,O="http://localhost:11434";function m(t=process.env){const n=t.BORG_OLLAMA_BASE_URL?.trim();return n?(/^[a-z][a-z0-9+.-]*:\/\//i.test(n)?n:`http://${n}`).replace(/\/+$/,""):O}function f(t){const n=t.indexOf(":");if(n<0)throw new Error(`invalid backend descriptor: ${t} (expected <kind>:<model>)`);const e=t.substring(0,n),a=t.substring(n+1);if(e!=="claude"&&e!=="ollama")throw new Error(`invalid backend kind: ${e} (expected claude or ollama)`);return{kind:e,model:a}}function _(t,n=m()){if(!t)return{set:{},unset:[]};const{kind:e,model:a}=f(t);return e==="claude"?{set:{ANTHROPIC_MODEL:a},unset:[]}:{set:{ANTHROPIC_BASE_URL:n,ANTHROPIC_MODEL:a,ANTHROPIC_AUTH_TOKEN:"ollama"},unset:["ANTHROPIC_API_KEY"]}}function k(t,n){const e=t.toLowerCase(),r=e.slice(e.lastIndexOf("/")+1).includes(":")?e:`${e}:latest`;return n.some(u=>{const c=u.toLowerCase();return c===r||c===e})}async function L(t,n,e=m()){if(!t)return{ok:!0};const{kind:a,model:r}=f(t);if(a==="claude")return{ok:!0};const c=e.includes("localhost")||e.includes("127.0.0.1")||e.includes("[::1]")?"start it (ollama serve)":"check the host is up and Ollama is listening there (OLLAMA_HOST=0.0.0.0)",d={ok:!1,message:`Ollama not reachable at ${e} \u2014 ${c}, or assimilate with a Claude backend.`},h=new AbortController,A=setTimeout(()=>h.abort(),3e3);try{const i=await n(`${e}/api/tags`,{signal:h.signal});if(!i.ok)return d;let l=null;try{const s=await i.json();s&&Array.isArray(s.models)&&(l=s.models.map(o=>(o?.name??o?.model??"").replace(/[\x00-\x1F\x7F]/g,"")).filter(o=>o.length>0))}catch{}if(l&&!k(r,l)){const s=l.length===0?"no models are pulled there":`available: ${l.slice(0,12).map(o=>o.length>64?`${o.slice(0,64)}\u2026`:o).join(", ")}${l.length>12?`, \u2026 (${l.length} total)`:""}`;return{ok:!1,message:`Ollama model '${r}' not found at ${e} (${s}) \u2014 pull it with: ollama pull ${r}`}}return{ok:!0}}catch{return d}finally{clearTimeout(A)}}export{p as BACKEND_DESCRIPTOR_REGEX,O as OLLAMA_DEFAULT_BASE_URL,L as checkBackendReachable,k as ollamaModelMatches,f as parseBackend,_ as resolveLaunchEnv,m as resolveOllamaBaseUrl};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "borgmcp",
3
- "version": "1.0.25",
3
+ "version": "1.0.27",
4
4
  "description": "Coordinate AI coding agents in shared cubes. Works with Claude Code and Codex. Create projects, assign roles, and share a live activity log.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",