@involvex/gitlurk-desktop 0.1.5
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 +37 -0
- package/dist/cli.js +31 -0
- package/dist/index.js +45 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @involvex/gitlurk-desktop
|
|
2
|
+
|
|
3
|
+
CLI companion for [GitLurk Desktop](https://github.com/involvex/gitlurk).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @involvex/gitlurk-desktop
|
|
9
|
+
# or
|
|
10
|
+
bun add -g @involvex/gitlurk-desktop
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
gitlurk --help
|
|
17
|
+
gitlurk --version
|
|
18
|
+
gitlurk . # open cwd in GitLurk Desktop
|
|
19
|
+
gitlurk gh run list # passthrough to GitHub CLI
|
|
20
|
+
gitlurk runs # shortcut for gh run list
|
|
21
|
+
gitlurk git config list --global
|
|
22
|
+
gitlurk install-desktop # download Windows installer from GitHub Releases
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Requires [GitHub CLI](https://cli.github.com) for `gh` commands.
|
|
26
|
+
|
|
27
|
+
## Desktop app
|
|
28
|
+
|
|
29
|
+
The npm package installs the CLI. Install the Windows desktop app with:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
gitlurk install-desktop
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or download the MSI/setup from [Releases](https://github.com/involvex/gitlurk/releases).
|
|
36
|
+
|
|
37
|
+
Set `GITLURK_DESKTOP_EXE` if the desktop binary is not on a standard path.
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// @bun
|
|
3
|
+
// ../shared/dist/protocol/parse-app-url.js
|
|
4
|
+
var ALLOWED_HOSTS = new Set(["github.com", "www.github.com"]);
|
|
5
|
+
function buildOpenRepoUrl(repoUrl, branch) {
|
|
6
|
+
const base = `mygit://openRepo/${repoUrl}`;
|
|
7
|
+
return branch ? `${base}?branch=${encodeURIComponent(branch)}` : base;
|
|
8
|
+
}
|
|
9
|
+
// src/cli.ts
|
|
10
|
+
var args = process.argv.slice(2);
|
|
11
|
+
var command = args[0];
|
|
12
|
+
function spawnDesktop(arg) {
|
|
13
|
+
const exe = process.env.MYGIT_DESKTOP_EXE ?? "C:\\Program Files\\MyGit Desktop\\mygit-desktop.exe";
|
|
14
|
+
Bun.spawn([exe, arg], { stdout: "inherit", stderr: "inherit" });
|
|
15
|
+
}
|
|
16
|
+
if (command === "open" && args[1]) {
|
|
17
|
+
spawnDesktop(`--open-local=${args[1]}`);
|
|
18
|
+
} else if (command === "clone" && args[1]) {
|
|
19
|
+
spawnDesktop(`--clone=${args[1]}`);
|
|
20
|
+
} else if (command === "url" && args[1]) {
|
|
21
|
+
spawnDesktop(buildOpenRepoUrl(args[1], args[2]));
|
|
22
|
+
} else {
|
|
23
|
+
console.log(`MyGit CLI
|
|
24
|
+
|
|
25
|
+
Usage:
|
|
26
|
+
mygit open <path> Open local repository
|
|
27
|
+
mygit clone <url> Clone repository
|
|
28
|
+
mygit url <repo-url> [branch] Open via protocol handler
|
|
29
|
+
`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{spawn as y}from"node:child_process";import{platform as lt}from"node:os";var k=null,ut="GitHub CLI (gh) not found. Install from https://cli.github.com";async function gt(){let t=lt()==="win32"?"where":"which";return new Promise((e)=>{let r=y(t,["gh"],{shell:!0}),n="";r.stdout.on("data",(o)=>{n+=o.toString()}),r.on("close",(o)=>{if(o!==0){e(null);return}let i=n.split(/\r?\n/).find((s)=>s.trim());e(i?.trim()??null)}),r.on("error",()=>e(null))})}async function d(){if(k)return k;let t=await gt();if(!t)throw Error(ut);return k=t,t}async function ft(t,e={}){let r=await d(),n=e.cwd??process.cwd();if(e.inheritStdio)return{stdout:"",stderr:"",exitCode:await a(t,n)};return new Promise((o,i)=>{let s=y(r,t,{cwd:n,shell:!1}),g="",l="";s.stdout.on("data",(c)=>{g+=c.toString()}),s.stderr.on("data",(c)=>{l+=c.toString()}),s.on("error",i),s.on("close",(c)=>{o({stdout:g,stderr:l,exitCode:c??1})})})}async function a(t,e=process.cwd()){let r=await d();return new Promise((n,o)=>{let i=y(r,t,{cwd:e,stdio:"inherit",shell:!1});i.on("error",o),i.on("close",(s)=>n(s??1))})}async function L(){try{let t=await ft(["--version"]);if(t.exitCode!==0)return null;return t.stdout.trim().split(`
|
|
3
|
+
`)[0]??null}catch{return null}}async function I(t=[],e){return a(["run","list",...t],e)}async function A(t=[],e){return a(["run","watch",...t],e)}async function T(t=[],e){return a(["repo","fork",...t],e)}async function O(t=[],e){return a(["repo","edit",...t],e)}async function _(t=[],e){return a(["release","create",...t],e)}async function U(t=[],e){return a(["alias",...t],e)}async function D(t=[],e){return a(["config",...t],e)}async function F(t=[],e){return a(["skill",...t],e)}import{spawn as H}from"node:child_process";import{existsSync as V,readFileSync as ht}from"node:fs";import{join as f,resolve as X}from"node:path";function j(t,e){let r=`gitlurk://openRepo/${t}`;return e?`${r}?branch=${encodeURIComponent(e)}`:r}import{existsSync as N,readFileSync as pt}from"node:fs";import{dirname as B,join as W}from"node:path";import{fileURLToPath as dt}from"node:url";function h(){let t=B(dt(import.meta.url));for(let e=0;e<12;e++){let r=W(t,"package.json");if(N(r))try{let o=JSON.parse(pt(r,"utf8")),i=W(t,"packages/app/package.json");if(o.workspaces&&N(i))return t}catch{}let n=B(t);if(n===t)break;t=n}return null}var p="gitlurk-desktop.exe";function mt(t){let e=f(t,".cargo/config.toml");if(!V(e))return null;try{return ht(e,"utf8").match(/^\s*target-dir\s*=\s*"([^"]+)"/m)?.[1]??null}catch{return null}}function M(t){let e=X(t);return[f(e,"release",p),f(e,"debug",p)]}function wt(t){let e=[],r=process.env.GITLURK_DESKTOP_EXE??process.env.MYGIT_DESKTOP_EXE;if(r)e.push(r);let n=process.env.CARGO_TARGET_DIR;if(n)e.push(...M(n));if(t){let o=mt(t);if(o)e.push(...M(o));e.push(f(t,"packages/app/src-tauri/target/release",p),f(t,"packages/app/src-tauri/target/debug",p))}return e}function xt(t){for(let e of wt(t))if(e&&V(e))return e;return null}function b(t){let e=h(),r=xt(e);if(r){console.log(`Launching ${r}`),console.log(` arg: ${t}`),H(r,[t],{detached:!0,stdio:"ignore",windowsHide:!1}).unref();return}if(!e)console.error("GitLurk Desktop executable not found and monorepo root could not be resolved.\nSet GITLURK_DESKTOP_EXE or CARGO_TARGET_DIR, or install with `gitlurk install-desktop`."),process.exit(1);console.log(`No ${p} found — launching via bun tauri from ${e}`),console.log(` arg: ${t}`),H("bun",["run","--filter","@gitlurk/app","tauri","dev","--release","--no-watch","--",t],{cwd:e,detached:!0,stdio:"ignore"}).unref()}function K(t){let e=X(t);b(`--open-local=${e}`)}function q(t){let e=t[0];if(!e||e===".")return K(e==="."?".":process.cwd()),!0;if(e==="open"&&t[1])return K(t[1]),!0;if(e==="clone"&&t[1])return b(`--clone=${t[1]}`),!0;if(e==="url"&&t[1])return b(j(t[1],t[2])),!0;return!1}async function J(){try{await d()}catch(t){console.error(t instanceof Error?t.message:String(t)),process.exit(1)}}async function z(t){await J();let e=t[0];if(e==="runs")process.exit(await I(t.slice(1)));if(e==="watch")process.exit(await A(t.slice(1)));if(e==="fork")process.exit(await T(t.slice(1)));if(e==="repo"&&t[1]==="edit")process.exit(await O(t.slice(2)));if(e==="release"&&t[1]==="create")process.exit(await _(t.slice(2)));if(e==="alias")process.exit(await U(t.slice(1)));if(e==="config")process.exit(await D(t.slice(1)));if(e==="skill")process.exit(await F(t.slice(1)))}async function Y(t){await J(),process.exit(await a(t))}function Q(t){if(!t)return!1;return t==="gh"||t==="runs"||t==="watch"||t==="fork"||t==="repo"||t==="release"||t==="alias"||t==="config"||t==="skill"}import{spawn as G}from"node:child_process";import kt from"node:fs";import P from"node:path";import{platform as C}from"node:os";var m=null;function yt(){let t=[P.join(process.cwd(),"resources","git","cmd","git.exe"),P.join(process.cwd(),"resources","git","bin","git.exe"),P.join(process.env.LOCALAPPDATA??"","GitLurk","git","cmd","git.exe")];for(let e of t)if(e&&kt.existsSync(e))return e;return null}async function bt(){if(m)return m;let t=await Pt();if(t)return m=t,t;let e=yt();if(e)return m=e,e;throw Error("Git not found. Install Git for Windows or bundle Portable Git in resources/git/")}async function Pt(){let t=C()==="win32"?"where":"which";return new Promise((e)=>{let r=G(t,["git"],{shell:!0}),n="";r.stdout.on("data",(o)=>{n+=o.toString()}),r.on("close",(o)=>{if(o!==0){e(null);return}let i=n.split(/\r?\n/).find((s)=>s.trim());e(i?.trim()??null)}),r.on("error",()=>e(null))})}async function w(t,e,r){let n=await bt();return new Promise((o,i)=>{let s=G(n,t,{cwd:e,env:{...process.env,GIT_TERMINAL_PROMPT:"0"}}),g="",l="";s.stdout.on("data",(c)=>{let u=c.toString();g+=u,r?.(u.trim())}),s.stderr.on("data",(c)=>{let u=c.toString();l+=u,r?.(u.trim())}),s.on("error",i),s.on("close",(c)=>{o({stdout:g,stderr:l,exitCode:c??1})})})}function x(t){return`--${t}`}function Ct(t){let e=t.trim();if(!e)return null;let r=e.match(/^file:(.+?)\s+(.+)$/);if(r){let o=r[2]??"",i=o.indexOf("=");if(i===-1)return null;return{origin:r[1],key:o.slice(0,i),value:o.slice(i+1)}}let n=e.indexOf("=");if(n===-1)return null;return{key:e.slice(0,n),value:e.slice(n+1)}}async function Z(t){let e=t.cwd??process.cwd(),r=t.scope??"local",n=["config",x(r),"--list"];if(t.showOrigin??r==="local")n.push("--show-origin");let o=await w(n,e);if(o.exitCode!==0)throw Error(o.stderr||"Failed to list git config");return o.stdout.split(/\r?\n/).map(Ct).filter((i)=>i!==null)}async function tt(t){let e=t.cwd??process.cwd(),r=t.scope??"local",n=await w(["config",x(r),t.key],e);if(n.exitCode!==0)return null;return n.stdout.trim()}async function et(t){let e=t.cwd??process.cwd(),r=t.scope??"local",n=await w(["config",x(r),t.key,t.value],e);if(n.exitCode!==0)throw Error(n.stderr||"Failed to set git config")}async function Gt(t){let e=t.cwd??process.cwd(),r=await w(["config",x(t.scope),"--path"],e);if(r.exitCode!==0)return null;return r.stdout.trim()||null}async function rt(t){let e=t.cwd??process.cwd(),r=t.scope??"global",n=await Gt({scope:r,cwd:e});if(!n)throw Error(`Could not resolve ${r} git config file path`);let i=(process.env.GIT_EDITOR??process.env.VISUAL??process.env.EDITOR??(C()==="win32"?"notepad":"vi")).split(/\s+/).filter(Boolean),s=i[0];if(!s)throw Error("No editor configured");await new Promise((g,l)=>{let c=G(s,[...i.slice(1),n],{cwd:e,stdio:"inherit",shell:C()==="win32"});c.on("error",l),c.on("close",(u)=>{if(u!==0){l(Error(`Editor exited with code ${u}`));return}g()})})}function Rt(t){let e="local",r=[];for(let n of t)if(n==="--global")e="global";else if(n==="--local")e="local";else if(n==="--system")e="system";else r.push(n);return{scope:e,remaining:r}}async function nt(t){if(t[0]!=="git"||t[1]!=="config")return!1;let e=t[2],{scope:r,remaining:n}=Rt(t.slice(3)),o=process.cwd();if(e==="list"){let i=await Z({scope:r,cwd:o});for(let s of i)if(s.origin)console.log(`${s.origin} ${s.key}=${s.value}`);else console.log(`${s.key}=${s.value}`);return!0}if(e==="get"){let i=n[0];if(!i)console.error("Usage: gitlurk git config get <key> [--global|--local]"),process.exit(1);let s=await tt({key:i,scope:r,cwd:o});if(s===null)process.exit(1);return console.log(s),!0}if(e==="set"){let i=n[0],s=n[1];if(!i||s===void 0)console.error("Usage: gitlurk git config set <key> <value> [--global|--local]"),process.exit(1);return await et({key:i,value:s,scope:r,cwd:o}),!0}if(e==="edit")return await rt({scope:r,cwd:o}),!0;console.error("Unknown git config subcommand. Use list, get, set, or edit."),process.exit(1)}function ot(t){return t[0]==="git"&&t[1]==="config"}import{spawn as Et}from"node:child_process";import{createWriteStream as vt}from"node:fs";import{mkdir as St}from"node:fs/promises";import{homedir as $t,tmpdir as Lt}from"node:os";import{join as R}from"node:path";import{pipeline as It}from"node:stream/promises";import{Readable as At}from"node:stream";var E="involvex/gitlurk",Tt=`https://api.github.com/repos/${E}/releases/latest`;function Ot(t){let e=t.find((r)=>r.name.toLowerCase().endsWith(".msi"));if(e)return e;return t.find((r)=>/setup\.exe$/i.test(r.name)||/-setup\.exe$/i.test(r.name))??null}async function it(t,e){let r=await fetch(t,{headers:{"User-Agent":"gitlurk-desktop-cli"},redirect:"follow"});if(!r.ok||!r.body)throw Error(`Download failed: ${r.status} ${r.statusText}`);await It(At.fromWeb(r.body),vt(e))}function _t(t){if(console.log("Launching installer…"),process.platform==="win32"){Et("cmd",["/c","start","",t],{detached:!0,stdio:"ignore"}).unref();return}console.log(`Installer downloaded to:
|
|
4
|
+
${t}
|
|
5
|
+
Open it manually on this platform.`)}async function st(){console.log(`Fetching latest release from ${E}…`);let t=await fetch(Tt,{headers:{"User-Agent":"gitlurk-desktop-cli",Accept:"application/vnd.github+json"}});if(!t.ok)throw Error(`Failed to fetch releases: ${t.status} ${t.statusText}`);let e=await t.json(),r=Ot(e.assets);if(!r)throw Error(`No MSI/setup.exe found in ${e.tag_name}. See https://github.com/${E}/releases`);let n=R($t(),".gitlurk","downloads");await St(n,{recursive:!0});let o=R(n,r.name);console.log(`Downloading ${r.name} (${e.tag_name})…`);try{await it(r.browser_download_url,o)}catch{o=R(Lt(),r.name),await it(r.browser_download_url,o)}console.log(`Saved to ${o}`),_t(o)}function v(){console.log(`GitLurk CLI
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
gitlurk [--help] [--version]
|
|
9
|
+
gitlurk Open current directory in GitLurk Desktop
|
|
10
|
+
gitlurk . Same as above (resolved absolute path)
|
|
11
|
+
gitlurk open <path> Open local repository
|
|
12
|
+
gitlurk clone <url> Clone repository
|
|
13
|
+
gitlurk url <repo-url> [branch] Open via protocol handler
|
|
14
|
+
|
|
15
|
+
GitHub CLI (requires gh: https://cli.github.com):
|
|
16
|
+
gitlurk gh <args...> Passthrough to gh
|
|
17
|
+
gitlurk runs [flags] gh run list
|
|
18
|
+
gitlurk watch [run-id] gh run watch
|
|
19
|
+
gitlurk fork [repo] gh repo fork
|
|
20
|
+
gitlurk repo edit ... gh repo edit
|
|
21
|
+
gitlurk release create ... gh release create
|
|
22
|
+
gitlurk alias ... gh alias
|
|
23
|
+
gitlurk config ... gh config
|
|
24
|
+
gitlurk skill ... gh skill
|
|
25
|
+
|
|
26
|
+
Git config:
|
|
27
|
+
gitlurk git config list [--global|--local|--system]
|
|
28
|
+
gitlurk git config get <key> [--global|--local]
|
|
29
|
+
gitlurk git config set <key> <value> [--global|--local]
|
|
30
|
+
gitlurk git config edit [--global|--local]
|
|
31
|
+
|
|
32
|
+
Desktop installer:
|
|
33
|
+
gitlurk install-desktop Download & launch latest Windows installer
|
|
34
|
+
|
|
35
|
+
Env:
|
|
36
|
+
GITLURK_DESKTOP_EXE Path to gitlurk-desktop.exe (optional)
|
|
37
|
+
CARGO_TARGET_DIR Cargo target dir; uses release/ then debug/
|
|
38
|
+
|
|
39
|
+
Install:
|
|
40
|
+
npm i -g @involvex/gitlurk-desktop
|
|
41
|
+
bun link (from repo root, for local development)
|
|
42
|
+
|
|
43
|
+
If no exe is found, falls back to:
|
|
44
|
+
bun run --filter @gitlurk/app tauri dev --release --no-watch -- <arg>
|
|
45
|
+
`)}import{existsSync as Ut,readFileSync as Dt}from"node:fs";import{dirname as Ft,join as S}from"node:path";import{fileURLToPath as jt}from"node:url";function ct(t){if(!Ut(t))return null;try{return JSON.parse(Dt(t,"utf8")).version??null}catch{return null}}function at(){let t=h();if(t){let r=ct(S(t,"package.json"));if(r)return r}let e=Ft(jt(import.meta.url));for(let r of[S(e,"..","package.json"),S(e,"..","..","package.json")]){let n=ct(r);if(n)return n}return"0.0.0"}function Nt(t){let e=[],r=!1,n=!1;for(let o of t)if(o==="-h"||o==="--help")r=!0;else if(o==="-V"||o==="--version")n=!0;else e.push(o);return{args:e,help:r,version:n}}async function Bt(){let t=process.argv.slice(2),e=t[0]==="--"?t.slice(1):t,{args:r,help:n,version:o}=Nt(e);if(o){console.log(`gitlurk ${at()}`);let i=await L();if(i)console.log(i);return}if(n||r[0]==="help"){v();return}if(r[0]==="install-desktop"){try{await st()}catch(i){console.error(i instanceof Error?i.message:String(i)),process.exit(1)}return}if(ot(r)){await nt(r);return}if(r[0]==="gh"){await Y(r.slice(1));return}if(Q(r[0])){await z(r);return}if(q(r))return;v(),process.exit(1)}Bt();
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@involvex/gitlurk-desktop",
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "GitLurk Desktop CLI — open repos, wrap gh, manage git config",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"gitlurk": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/involvex/gitlurk.git",
|
|
16
|
+
"directory": "packages/cli"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://github.com/involvex/gitlurk#readme",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/involvex/gitlurk/issues"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"git",
|
|
24
|
+
"github",
|
|
25
|
+
"gh",
|
|
26
|
+
"desktop",
|
|
27
|
+
"cli",
|
|
28
|
+
"gitlurk"
|
|
29
|
+
],
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"dev": "bun run src/cli.ts",
|
|
36
|
+
"build": "bun build src/index.ts --outdir dist --target node --minify && node ./scripts/postbuild.mjs",
|
|
37
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
38
|
+
"prepublishOnly": "bun run build"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=20"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@gitlurk/gh": "workspace:*",
|
|
46
|
+
"@gitlurk/git": "workspace:*",
|
|
47
|
+
"@gitlurk/shared": "workspace:*",
|
|
48
|
+
"typescript": "^5.8.3"
|
|
49
|
+
}
|
|
50
|
+
}
|