@dowse-dev/cli 0.1.0 → 0.1.1
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/LICENSE +21 -0
- package/README.md +4 -0
- package/dist/cli.js +2 -2
- package/package.json +4 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dowse
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -43,3 +43,7 @@ Each repair has `unattended_safe`. It's `true` when the repair only changes the
|
|
|
43
43
|
The skill goes in `~/.agents/skills/dowse`, which Codex, Cursor, VS Code and Gemini CLI read, in `~/.claude/skills/dowse` for Claude Code, and in `~/.hermes/skills/dowse` for Hermes. It holds no key.
|
|
44
44
|
|
|
45
45
|
If you edit the skill, your copy is kept, and `disconnect` leaves it too. Anything else already named `dowse` there is left alone and reported.
|
|
46
|
+
|
|
47
|
+
## License
|
|
48
|
+
|
|
49
|
+
MIT. This covers the installer only; the Dowse service has its own [terms](https://dowse.dev/terms).
|
package/dist/cli.js
CHANGED
|
@@ -222,7 +222,7 @@ Use \`basic\` unless a basic attempt wasn't good enough.
|
|
|
222
222
|
`,Dl=e=>`<!-- dowse-cli:${Ml.default.createHash("sha256").update(e).digest("hex")} -->`,Il=`${ql}${Dl(ql)}
|
|
223
223
|
`,dp=/^([\s\S]*)<!-- dowse-cli:([0-9a-f]{64}) -->\n?$/,jt=e=>oe.default.join(e,T,"SKILL.md"),Pl=e=>{try{return Te.default.lstatSync(e).isSymbolicLink()}catch{return!1}};function Zr(e,t){return V("skill_not_writable",`Could not write the Dowse skill under ${e}. The MCP server is still set up.`,$(re(t),`Make ${ie(e)} writable, then run this. Or add --skip-skills.`))}function ei(e,t){if(Pl(oe.default.dirname(e))||Pl(e))return{found:"foreign"};let n;try{n=Te.default.readFileSync(e,"utf8")}catch(r){if(r.code==="ENOENT")return{found:"absent"};throw Zr(oe.default.dirname(e),t)}let s=dp.exec(n);return s?{found:Dl(s[1]??"").includes(s[2]??"")?"ours":"edited",text:n}:{found:"foreign"}}function jl(e,t){let n=jt(e),{found:s,text:r}=ei(n,t);if(s==="foreign")throw V("skill_conflict",`${oe.default.dirname(n)} is not a Dowse skill this tool installed, so it was left alone. The MCP server is still set up.`,$(re(t),`Rename or remove ${ie(oe.default.dirname(n))}, then run this. Or add --skip-skills.`));if(s==="edited")return"kept_edited";if(r===Il)return"unchanged";try{Te.default.mkdirSync(oe.default.dirname(n),{recursive:!0}),Br(n,Il,420)}catch{throw Zr(e,t)}return s==="absent"?"installed":"updated"}function Rl(e,t){let n=jt(e),{found:s}=ei(n,t);if(s==="edited")return"kept_edited";if(s!=="ours")return"none";try{Te.default.rmSync(n),Te.default.readdirSync(oe.default.dirname(n)).length===0&&Te.default.rmdirSync(oe.default.dirname(n))}catch{throw Zr(e,t)}return"removed"}function ti(e,t){let{found:n}=ei(jt(e),t);return n==="ours"||n==="edited"}function ni(e){return e.agent?Ce.filter(t=>t.name===e.agent):Ce}var Bl=(e,t)=>({agent:e.name,label:e.label,installed:t,config_path:e.configPath(),problems:[]});function Kl(){return I("no_agents_found","No supported coding agent was found on this computer.",$(`${R} connect --all`,"Install Claude Code, Cursor, Codex, VS Code, Gemini CLI or Hermes, then run this."))}function hp(e){return I("agent_not_installed",`${e.label} was not found on this computer, so nothing was changed.`,$(re(e.name),`Install ${e.label}, then run this.`))}function Rt(e,t,n,s){return{ok:s.length===0&&n.every(i=>i.problems.length===0),command:e,...t?{url:t}:{},agents:n,problems:s}}function pp(e,t,n){try{n()}catch(s){if(!(s instanceof W))throw s;t.status="failed",t.problems.push(s.problem)}}function Fl(e,t){let n=[],s=[];for(let r of ni(e)){let i=r.isInstalled(),o=Bl(r,i);if(n.push(o),!i){o.status="not_installed",e.agent&&o.problems.push(hp(r));continue}pp(r,o,()=>{let a=t(r);o.status=a.status,a.backup_path&&(o.backup_path=a.backup_path)})}return!e.agent&&!n.some(r=>r.installed)&&s.push(Kl()),{reports:n,problems:s}}function mp(e){let t=new Map;for(let n of e){if(n.problems.length>0||!["connected","updated","unchanged"].includes(n.status??""))continue;let s=Ce.find(i=>i.name===n.agent).skillsDir(),r=t.get(s);if(!r){try{r=jl(s,n.agent)}catch(i){if(!(i instanceof W))throw i;r=i}t.set(s,r)}r instanceof W?(n.skill="failed",n.problems.push(r.problem)):(n.skill=r,n.skill_path=jt(s))}}function gp(e,t){let n=r=>{let i=t.find(o=>o.agent===r.name);if(i)return i.problems.length>0;try{return r.inspect().configured}catch{return!0}},s=new Map;for(let r of e){let i=r.skillsDir(),o=s.get(i);if(!o){let l=Ce.filter(c=>c.skillsDir()===i);try{l.some(n)?o=ti(i,r.name)?"kept_shared":"none":o=Rl(i,r.name)}catch(c){if(!(c instanceof W))throw c;o=c}s.set(i,o)}let a=t.find(l=>l.agent===r.name);a.installed&&(o instanceof W?a.problems.push(o.problem):o!=="none"&&(a.skill=o))}}function He(e,t){return I(e,t,$(`${R} connect --all --api-key <key>`,"Get a working API key from your Dowse dashboard, then run this with it."))}async function Ul(e){let t=o=>Rt("connect",e.url,[],[o]);if(!e.key)return t(He("missing_key",`No API key. Pass --api-key or set ${Z}. Nothing was changed.`));let n=e.key;if(!Mt(n))return t(He("invalid_key","That API key has characters no key can have. Nothing was changed."));let s=await zr(e.url,n);if(s==="invalid")return t(He("invalid_key","The server rejected that API key. Nothing was changed."));if(s!=="valid")return t(I("key_check_failed",`Could not check the key against ${e.url}, so nothing was changed.`,$(`${R} connect --all`,"Check your connection to the server, then run this again.")));let{reports:r,problems:i}=Fl(e,o=>o.connect(e.url,n));return e.skipSkills||mp(r),Rt("connect",e.url,r,i)}function xl(e){let{reports:t,problems:n}=Fl(e,s=>s.disconnect());return gp(ni(e),t),Rt("disconnect",void 0,t,n)}function yp(){for(let e of Ce)try{let t=e.inspect();if(!t.key||!Mt(t.key))continue;let n=t.url??Fe;if(!Dt(n))return{key:t.key,url:n}}catch(t){if(!(t instanceof W))throw t}}async function Yl(e,t,n){let s=c=>Rt(e,void 0,[],[c]),r=t.key?void 0:yp(),i=t.key??r?.key,o=r?.url??t.url;if(!i)return s(He("missing_key",`No API key. Run connect first, pass --api-key, or set ${Z}.`));if(!Mt(i))return s(He("invalid_key","That API key has characters no key can have."));let a=await Ll(o,i,`/v1/${e}`,n);if(a.kind==="ok"){let{results:c,failed_results:f}=a.data;if(e==="extract"&&Array.isArray(c)&&c.length===0&&Array.isArray(f)&&f.length>0){let u=f.map(p=>String(p.url??"")).filter(Boolean),d=I("extract_failed",`Could not read: ${u.join(", ")}`,$(`${R} extract ${u.map(ie).join(" ")} --depth advanced`,"Advanced handles harder pages and costs more if it succeeds. The pages may also just be unavailable."));return{...s(d),failed_results:f,...a.data.usage?{usage:a.data.usage}:{}}}return{...a.data,ok:!0,command:e}}let l=c=>$(`${R} doctor`,c);if(a.kind==="unreachable")return s(I("server_unreachable",`Could not reach the server at ${o}.`,l("Check your connection, then see what doctor reports.")));if(a.status===401||a.status===403)return s(He("invalid_key","The server rejected the API key."));if(a.status===432)return s(I("insufficient_credits","This account is out of credits.",l("Add credits in your Dowse dashboard, then run the same command again.")));if(a.status===429){let c=/^\d+$/.test(a.retryAfter??"")?a.retryAfter:"1";return s(I("rate_limited",`Too many requests. Wait ${c} s and try again.`,{command:`sleep ${c}`,description:"Wait, then run the same command again.",unattended_safe:!0}))}return a.status===400?s(I("bad_request",`The server refused the request${a.error?`: ${a.error}`:"."}`,$(`${R} --help`,"Fix the command, then run it again."))):s(I("server_error",`The server could not answer (HTTP ${a.status}).`,l("Try again shortly. If it keeps failing, see what doctor reports.")))}async function Vl(e){let t=new Map,n=new Map,s=(a,l,c)=>{let f=a.get(l);return f||a.set(l,f=c()),f},r=async(a,l)=>{let c=`${a}
|
|
224
224
|
${l}`,[f,u]=await Promise.all([s(t,c,()=>zr(a,l)),s(n,c,()=>Xr(a,l))]);return{usage:f,mcp:u}},i=await Promise.all(ni(e).map(async a=>{let l=a.isInstalled(),c=Bl(a,l);if(c.configured=!1,c.key_valid=null,c.server_reachable=null,c.skill_installed=!1,!l)return c;try{c.skill_installed=ti(a.skillsDir(),a.name)}catch(u){if(!(u instanceof W))throw u}let f=async u=>(await s(n,`${u}
|
|
225
|
-
`,()=>Xr(u))).reachable;try{let u=a.inspect();if(c.configured=u.configured,!u.configured)return c.server_reachable=await f(e.url),c.problems.push(I("not_configured",`${a.label} is installed, but Dowse is not set up in it.`,{command:re(a.name),description:`Adds only the Dowse entry. Needs the key in ${Z} or --api-key.`,unattended_safe:!0})),c;let d=u.url??e.url;c.url=d;let p=Dt(d);if(p)return c.problems.push(I("insecure_url",`${p} The key was not sent.`,$(`${R} connect --agent ${a.name} --api-key <key>`,"This resets the entry to the default server. Check first that you did not set this URL on purpose."))),c;if(u.keyIsReference)c.server_reachable=await f(d);else if(!u.key)c.server_reachable=await f(d),c.problems.push(I("key_missing",`The Dowse entry in ${a.label} has no API key.`,{command:re(a.name),description:`Sets the key on the Dowse entry only. Needs the key in ${Z} or --api-key.`,unattended_safe:!0}));else if(!Mt(u.key))c.server_reachable=await f(d),c.key_valid=!1;else{let g=await r(d,u.key);c.server_reachable=g.mcp.reachable,g.usage==="invalid"||g.mcp.key==="invalid"?c.key_valid=!1:g.usage==="valid"&&g.mcp.key==="valid"&&(c.key_valid=!0)}c.key_valid===!1&&c.problems.push(I("invalid_key",`The API key in ${a.label} was rejected.`,$(`${R} connect --agent ${a.name} --api-key <key>`,"Get a working key from your Dowse dashboard, then run this with it."))),c.server_reachable===!1&&c.problems.push(I("server_unreachable",`Could not reach the server at ${d}.`,$(`curl -sS -o /dev/null -w "%{http_code}\\n" ${ie(d)}`,"Check your connection, proxy or firewall. Any HTTP status below 500 means the server is reachable.")))}catch(u){if(!(u instanceof W))throw u;c.problems.push(u.problem)}return c})),o=!e.agent&&!i.some(a=>a.installed)?[Kl()]:[];return Rt("doctor",e.url,i,o)}var Wl=["connect","doctor","disconnect","search","extract"],Hl={connect:["--all","--agent","--api-key","--url","--skip-skills"],doctor:["--agent","--url"],disconnect:["--all","--agent"],search:["--api-key","--url","--max-results","--depth","--topic","--country","--answer"],extract:["--api-key","--url","--depth"]},Gl=["search","extract"],bp=`dowse 0.1.
|
|
225
|
+
`,()=>Xr(u))).reachable;try{let u=a.inspect();if(c.configured=u.configured,!u.configured)return c.server_reachable=await f(e.url),c.problems.push(I("not_configured",`${a.label} is installed, but Dowse is not set up in it.`,{command:re(a.name),description:`Adds only the Dowse entry. Needs the key in ${Z} or --api-key.`,unattended_safe:!0})),c;let d=u.url??e.url;c.url=d;let p=Dt(d);if(p)return c.problems.push(I("insecure_url",`${p} The key was not sent.`,$(`${R} connect --agent ${a.name} --api-key <key>`,"This resets the entry to the default server. Check first that you did not set this URL on purpose."))),c;if(u.keyIsReference)c.server_reachable=await f(d);else if(!u.key)c.server_reachable=await f(d),c.problems.push(I("key_missing",`The Dowse entry in ${a.label} has no API key.`,{command:re(a.name),description:`Sets the key on the Dowse entry only. Needs the key in ${Z} or --api-key.`,unattended_safe:!0}));else if(!Mt(u.key))c.server_reachable=await f(d),c.key_valid=!1;else{let g=await r(d,u.key);c.server_reachable=g.mcp.reachable,g.usage==="invalid"||g.mcp.key==="invalid"?c.key_valid=!1:g.usage==="valid"&&g.mcp.key==="valid"&&(c.key_valid=!0)}c.key_valid===!1&&c.problems.push(I("invalid_key",`The API key in ${a.label} was rejected.`,$(`${R} connect --agent ${a.name} --api-key <key>`,"Get a working key from your Dowse dashboard, then run this with it."))),c.server_reachable===!1&&c.problems.push(I("server_unreachable",`Could not reach the server at ${d}.`,$(`curl -sS -o /dev/null -w "%{http_code}\\n" ${ie(d)}`,"Check your connection, proxy or firewall. Any HTTP status below 500 means the server is reachable.")))}catch(u){if(!(u instanceof W))throw u;c.problems.push(u.problem)}return c})),o=!e.agent&&!i.some(a=>a.installed)?[Kl()]:[];return Rt("doctor",e.url,i,o)}var Wl=["connect","doctor","disconnect","search","extract"],Hl={connect:["--all","--agent","--api-key","--url","--skip-skills"],doctor:["--agent","--url"],disconnect:["--all","--agent"],search:["--api-key","--url","--max-results","--depth","--topic","--country","--answer"],extract:["--api-key","--url","--depth"]},Gl=["search","extract"],bp=`dowse 0.1.1: connect Dowse web search to the coding agents on this computer.
|
|
226
226
|
|
|
227
227
|
Usage:
|
|
228
228
|
${R} connect [--all | --agent <name>] --api-key <key>
|
|
@@ -253,5 +253,5 @@ Search and extract:
|
|
|
253
253
|
`))}function Ap(e){let t=[];typeof e.answer=="string"&&e.answer&&t.push(e.answer,"");let n=Array.isArray(e.results)?e.results:[];n.forEach((i,o)=>{t.push(`${o+1}. ${i.title??i.url??""}`,` ${i.url??""}`,` ${i.content??""}`,"")}),n.length===0&&t.push("No results.","");let s=Array.isArray(e.failed_results)?e.failed_results:[];for(let i of s)t.push(`Failed: ${i.url??""}${i.error?` (${i.error})`:""}`);let r=e.usage?.credits;return typeof r=="number"&&t.push(`${r} credit${r===1?"":"s"} used.`),`${t.join(`
|
|
254
254
|
`).trimEnd()}
|
|
255
255
|
`}function Op(e,t){if(!e.ok){Gn(e,t);return}let n=Wr(e);process.stdout.write(Wr(t?Ap(n):`${JSON.stringify(n,null,2)}
|
|
256
|
-
`))}function Np(e){return e.command==="extract"?{urls:e.words,...e.depth?{extract_depth:e.depth}:{}}:{query:e.words.join(" "),...e.maxResults?{max_results:e.maxResults}:{},...e.depth?{search_depth:e.depth}:{},...e.topic?{topic:e.topic}:{},...e.country?{country:e.country}:{},...e.answer?{include_answer:!0}:{}}}async function Ep(e){let t=e.includes("--pretty");Ye(process.env[Z]);let n;try{if(n=wp(e),n.version)return process.stdout.write(`0.1.
|
|
256
|
+
`))}function Np(e){return e.command==="extract"?{urls:e.words,...e.depth?{extract_depth:e.depth}:{}}:{query:e.words.join(" "),...e.maxResults?{max_results:e.maxResults}:{},...e.depth?{search_depth:e.depth}:{},...e.topic?{topic:e.topic}:{},...e.country?{country:e.country}:{},...e.answer?{include_answer:!0}:{}}}async function Ep(e){let t=e.includes("--pretty");Ye(process.env[Z]);let n;try{if(n=wp(e),n.version)return process.stdout.write(`0.1.1
|
|
257
257
|
`),0;if(n.help)return process.stdout.write(bp),0;Sp(n)}catch(a){if(!(a instanceof K))throw a;let l=I("usage",a.message,$(`${R} --help`,"See the available commands and options."));return Gn({ok:!1,command:"usage",agents:[],problems:[l]},t),2}let r={url:n.url??Fe,...n.agent?{agent:n.agent}:{}},i=n.key??process.env[Z];if(n.command==="search"||n.command==="extract"){let a=await Yl(n.command,{...r,...i?{key:i}:{}},Np(n));return Op(a,n.pretty),a.ok?0:1}let o;return n.command==="connect"?o=await Ul({...r,...i?{key:i}:{},skipSkills:n.skipSkills}):n.command==="doctor"?o=await Vl(r):o=xl(r),Gn(o,n.pretty),o.ok?0:1}Ep(process.argv.slice(2)).then(e=>{process.exitCode=e},e=>{let t=e instanceof Error?e.message:"Unexpected error",n=I("internal_error",t,$(`${R} doctor`,"Run doctor to see the current state. Report this if it repeats."));Gn({ok:!1,command:"error",agents:[],problems:[n]},process.argv.includes("--pretty")),process.exitCode=1});
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dowse-dev/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Connect Dowse web search to the coding agents on your computer.",
|
|
5
|
-
"
|
|
5
|
+
"homepage": "https://dowse.dev",
|
|
6
|
+
"license": "MIT",
|
|
6
7
|
"publishConfig": {
|
|
7
8
|
"access": "public"
|
|
8
9
|
},
|
|
@@ -10,6 +11,7 @@
|
|
|
10
11
|
"dowse": "dist/cli.js"
|
|
11
12
|
},
|
|
12
13
|
"files": [
|
|
14
|
+
"LICENSE",
|
|
13
15
|
"dist/cli.js",
|
|
14
16
|
"dist/THIRD_PARTY_NOTICES.txt"
|
|
15
17
|
],
|