@ghship/cli 1.0.2 → 1.0.4

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
@@ -1,7 +1,10 @@
1
1
  # GHShip npm distribution
2
2
 
3
- Go CLI and home AI skill plus per-project integration. Package includes native
4
- binaries for macOS/Linux/Windows × amd64/arm64, with no postinstall downloads.
3
+ Go CLI and home AI skill plus per-project integration. npm installs the native
4
+ binary package matching the Node.js operating system and architecture for
5
+ macOS/Linux/Windows × x64/arm64, with no postinstall downloads. Optional
6
+ dependencies must be enabled and the matching package must be available from the
7
+ configured registry. An emulated Node.js runtime selects its own architecture.
5
8
 
6
9
  After publication: `npx @ghship/cli@latest install` installs the home skill, same-version
7
10
  persistent CLI, then enters project initialization. `--user-only` stops after user
@@ -18,7 +21,8 @@ expiry when GitHub returns it, otherwise unknown. Tokens never belong in project
18
21
 
19
22
  For a downloaded npm tarball, use:
20
23
  `npm exec --package /absolute/ghship.tgz -- ghship install --package-source /absolute/ghship.tgz`.
21
- Add `--user-only` to install shared tools without initializing a project.
24
+ The configured registry must provide the same-version platform package. Add
25
+ `--user-only` to install shared tools without initializing a project.
22
26
  Registry commands require the selected version to be published.
23
27
 
24
28
  Detailed Chinese tutorial and AI skill: https://github.com/ghship/cli
package/checksums.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
- "bin/darwin-amd64/ghship": "7b19d36ac24a2d5d183060364fffbba7596e1748eb379b9e74590ef375ed4530",
3
- "bin/darwin-arm64/ghship": "3430a35901869edeca99452186c2360baae6c5df467595d0e9f0835e4fd57a91",
4
- "bin/linux-amd64/ghship": "2659abe45e6f8fb073f4b258665cf788a00b43c60a31410e824d4233acff7e2e",
5
- "bin/linux-arm64/ghship": "04d9147c1f8b931b57ddbc5fe80cf9a27feb5bd726e992e42f66941c0b36ab18",
6
- "bin/windows-amd64/ghship.exe": "78495cfc5d803a3955ffb41440ae2e78f9d75be096595e8ea21a1f37a96eeecc",
7
- "bin/windows-arm64/ghship.exe": "b43c1717508b2bee79a6732bfc481872814cf06fa7a6310b967412d64c54d95b"
2
+ "darwin-arm64": "bdce86aa2ebb4bca9ee4e149cce2b29d51557dfeee7f732b2dcd6d1458f125a6",
3
+ "darwin-x64": "c0b71ad5b0d9e338cb9836b8572e9caacbad1a710965df0af88665d68dc100da",
4
+ "linux-arm64": "b3607bc943a8b01a5e8a164a21026766442cd1a7fae14b2703afcfdd1afa97f3",
5
+ "linux-x64": "2ba3458d178f81abc11478fa92128e76fcc61567b9abc4a8d1931fcaa6363f02",
6
+ "win32-arm64": "68a9bb61d9f1ee7147f2bc7eacc2c377fd25f2016b4d911b818db3d8666e2f4c",
7
+ "win32-x64": "dc1d6ebf166634222f919668f1b7803c2bf1c2c6d6df2d460c95b7bb48f2d85d"
8
8
  }
package/package.json CHANGED
@@ -1,16 +1,23 @@
1
1
  {
2
2
  "name": "@ghship/cli",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Go CLI and AI skill for native GitHub delivery pipelines",
5
5
  "bin": {
6
6
  "ghship": "run.cjs"
7
7
  },
8
8
  "files": [
9
9
  "run.cjs",
10
- "bin",
11
10
  "README.md",
12
11
  "checksums.json"
13
12
  ],
13
+ "optionalDependencies": {
14
+ "@ghship/cli-darwin-x64": "1.0.4",
15
+ "@ghship/cli-darwin-arm64": "1.0.4",
16
+ "@ghship/cli-linux-x64": "1.0.4",
17
+ "@ghship/cli-linux-arm64": "1.0.4",
18
+ "@ghship/cli-win32-x64": "1.0.4",
19
+ "@ghship/cli-win32-arm64": "1.0.4"
20
+ },
14
21
  "engines": {
15
22
  "node": ">=18"
16
23
  },
package/run.cjs CHANGED
@@ -4,11 +4,31 @@ const fs = require('node:fs');
4
4
  const os = require('node:os');
5
5
  const crypto = require('node:crypto');
6
6
  const { spawn, spawnSync } = require('node:child_process');
7
- function binaryPath(platform = process.platform, arch = process.arch) {
8
- const os = {darwin:'darwin', linux:'linux', win32:'windows'}[platform];
9
- const cpu = {x64:'amd64', arm64:'arm64'}[arch];
10
- if (!os || !cpu) throw new Error(`Unsupported platform: ${platform}/${arch}`);
11
- return path.join(__dirname, 'bin', `${os}-${cpu}`, os === 'windows' ? 'ghship.exe' : 'ghship');
7
+ function singlePack(value,name) {
8
+ let record;
9
+ if(Array.isArray(value)) {if(value.length!==1)throw new Error('Unexpected npm pack result');record=value[0];}
10
+ else if(value&&typeof value==='object'&&Object.keys(value).length===1&&Object.hasOwn(value,name))record=value[name];
11
+ if(!record||typeof record!=='object'||record.name!==name||typeof record.version!=='string')throw new Error('Unexpected npm pack result');
12
+ return record;
13
+ }
14
+ function platformInfo(platform = process.platform, arch = process.arch) {
15
+ if (!['darwin','linux','win32'].includes(platform) || !['x64','arm64'].includes(arch)) throw new Error(`Unsupported platform: ${platform}/${arch}`);
16
+ return {target:`${platform}-${arch}`,name:`@ghship/cli-${platform}-${arch}`,binary:platform==='win32'?'ghship.exe':'ghship'};
17
+ }
18
+ function binaryPath(platform = process.platform, arch = process.arch, root = __dirname) {
19
+ const info=platformInfo(platform,arch);
20
+ const meta=JSON.parse(fs.readFileSync(path.join(root,'package.json'),'utf8'));
21
+ let manifest;try {manifest=require.resolve(`${info.name}/package.json`,{paths:[root]});}
22
+ catch (_) {throw new Error(`Platform package ${info.name}@${meta.version} is missing; enable optional dependencies and reinstall @ghship/cli@${meta.version}`);}
23
+ const allowed=[path.join(root,'node_modules',info.name),path.join(path.dirname(path.dirname(root)),info.name)];
24
+ if(!allowed.some(dir=>path.resolve(path.dirname(manifest))===path.resolve(dir)))throw new Error('Platform package is outside the installation; preserved');
25
+ noLinks(manifest);
26
+ const platformMeta=JSON.parse(fs.readFileSync(manifest,'utf8'));
27
+ if(platformMeta.name!==info.name||platformMeta.version!==meta.version||!platformMeta.os?.includes(platform)||!platformMeta.cpu?.includes(arch))throw new Error('Platform package does not match CLI version or platform; preserved');
28
+ const executable=path.join(path.dirname(manifest),'bin',info.binary);noLinks(executable);
29
+ const checks=JSON.parse(fs.readFileSync(path.join(root,'checksums.json'),'utf8'));
30
+ if(!checks[info.target]||hashFile(executable)!==checks[info.target])throw new Error('Platform binary missing or checksum mismatch; preserved');
31
+ return executable;
12
32
  }
13
33
  function npmCommand() {
14
34
  if (process.env.npm_execpath && fs.existsSync(process.env.npm_execpath)) return [process.execPath, [process.env.npm_execpath]];
@@ -53,8 +73,10 @@ function observePackage(root,meta,existing,replacingVerifiedReceipt=false) {
53
73
  const directory=path.join(root,meta.name); noLinks(directory);
54
74
  const metadata=JSON.parse(fs.readFileSync(path.join(directory,'package.json'),'utf8'));
55
75
  if(metadata.name!==meta.name||metadata.version!==existing.version||metadata.bin?.ghship!=='run.cjs')throw new Error('Unrecognized global package; preserved');
56
- const relative=path.relative(__dirname,binaryPath());
57
- const observed={root,name:meta.name,version:metadata.version,launcher:hashFile(path.join(directory,'run.cjs')),binary:hashFile(path.join(directory,relative))};
76
+ const info=platformInfo();const legacyOS={darwin:'darwin',linux:'linux',win32:'windows'}[process.platform];const legacyArch={x64:'amd64',arm64:'arm64'}[process.arch];
77
+ const legacy=path.join(directory,'bin',`${legacyOS}-${legacyArch}`,info.binary);
78
+ const installedBinary=fs.existsSync(legacy)?legacy:binaryPath(process.platform,process.arch,directory);noLinks(installedBinary);
79
+ const observed={root,name:meta.name,version:metadata.version,launcher:hashFile(path.join(directory,'run.cjs')),binary:hashFile(installedBinary)};
58
80
  const current=observed.version===meta.version&&observed.launcher===hashFile(path.join(__dirname,'run.cjs'))&&observed.binary===hashFile(binaryPath());
59
81
  const receipt=packageReceipt(root);noLinks(receipt);
60
82
  let recorded=null;try{recorded=JSON.parse(fs.readFileSync(receipt,'utf8'));}catch(error){if(error.code!=='ENOENT')throw new Error('Invalid installation receipt; preserved');}
@@ -73,7 +95,7 @@ function lifecycle(executable,args) {
73
95
  const action=args[0], meta=require('./package.json'); let source=`${meta.name}@${meta.version}`;
74
96
  const passed=args.slice(1); const sourceIndex=passed.indexOf('--package-source');
75
97
  if(sourceIndex>=0) {if(!passed[sourceIndex+1])throw new Error('--package-source requires a local tarball');source=path.resolve(passed[sourceIndex+1]);passed.splice(sourceIndex,2);
76
- const packed=JSON.parse(npm(['pack',source,'--dry-run','--json'],true));if(packed.length!==1||packed[0].name!==meta.name||packed[0].version!==meta.version)throw new Error('Local package source must match this exact package and version');
98
+ const packed=singlePack(JSON.parse(npm(['pack',source,'--dry-run','--json'],true)),meta.name);if(packed.version!==meta.version)throw new Error('Local package source must match this exact package and version');
77
99
  }
78
100
  const lifecycleArgs=[];const initArgs=[];let userOnly=false;
79
101
  const initValues=new Set(['--project','--repo','--author','--reviewer','--admin','--acceptance-pr']);
@@ -115,7 +137,7 @@ function lifecycle(executable,args) {
115
137
  const verified=JSON.parse(npm(['list','-g',meta.name,'--depth=0','--json'],true));
116
138
  if(!verified.dependencies||verified.dependencies[meta.name]?.version!==meta.version)throw new Error('Installed CLI version does not match skill; installation incomplete');
117
139
  const persisted=observePackage(root,meta,verified.dependencies[meta.name],!!observation);if(!persisted.current)throw new Error('Persistent CLI content does not match this package');
118
- const runtimeVersion=go(path.join(root,meta.name,path.relative(__dirname,executable)),['version'],true);
140
+ const runtimeVersion=go(binaryPath(process.platform,process.arch,path.join(root,meta.name)),['version'],true);
119
141
  if(runtimeVersion.code!==0||runtimeVersion.stdout.trim()!==meta.version)throw new Error('Persistent CLI cannot run the expected version');
120
142
  saveReceipt(root,persisted.observed);
121
143
  if(action==='update'||userOnly){console.log(JSON.stringify({userInstalled:true,version:meta.version,projectInitialized:false}));return 0;}
@@ -123,7 +145,7 @@ function lifecycle(executable,args) {
123
145
  let result;try{result=JSON.parse(init.stdout);}catch(_){result={ready:false,missing:['Initialization failed before producing a result'],next:['ghship init --json']};}
124
146
  result.userInstalled=true;result.version=meta.version;console.log(JSON.stringify(result));return init.code;
125
147
  }
126
- module.exports = {binaryPath};
148
+ module.exports = {binaryPath,platformInfo,singlePack};
127
149
  if(require.main===module) {
128
150
  try {
129
151
  const executable=binaryPath();if(!fs.existsSync(executable))throw new Error('Package missing Go binary; reinstall the package');
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file