@petercjl/codex-own 0.1.0 → 0.2.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.
- package/README.md +116 -6
- package/capabilities.json +11 -1
- package/lib/manager.mjs +23 -16
- package/lib/network.mjs +62 -0
- package/lib/npm-assets.mjs +58 -0
- package/package.json +138 -1
- package/release.json +122 -7
- package/skills/codex-own/SKILL.md +4 -1
package/README.md
CHANGED
|
@@ -1,13 +1,123 @@
|
|
|
1
1
|
# codex-own
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
独立桌面应用的 npm 安装与更新工具。支持 Mac ARM64 和 Windows x64。管理工具和桌面资源均从 npm 下载。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## 在 Mac 上通过 Terminal 安装和更新
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
适用于 Apple 芯片 Mac(ARM64)。从 Node.js 官网安装 LTS 版本(要求 Node.js 22 或以上,包含 npm),重新打开 Terminal。建议预留 10 GB 空间,用于下载、解压和备份。
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
[Node.js 官方下载](https://nodejs.org/en/download)
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
安装验收只需完成前三步。无需 Agent、API Key 或模型登录。
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
第一步:检查环境并安装 npm 管理工具。以下工具安装在当前用户的 ~/.local。逐段执行,每一步成功后再继续。
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
node --version
|
|
17
|
+
npm --version
|
|
18
|
+
npm install --global --prefix "$HOME/.local" @petercjl/codex-own@latest
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
第二步:安装桌面程序。管理工具按当前平台从 npm 下载资源分片,依次显示下载进度、校验、解压和安装阶段。
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
"$HOME/.local/bin/codex-own" install --yes
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
若已有手动安装的 codex-own,请先退出应用,将第二步命令替换为以下命令,备份后接管原安装。
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
"$HOME/.local/bin/codex-own" install --adopt --yes
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
第三步:检查安装结果。安装结果应显示 ok: true、downloadSource: registry.npmjs.org;doctor 中 installed 有版本且 lock 为 false,version 显示管理工具和桌面版本。应用位于 /Applications/codex-own.app。
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
"$HOME/.local/bin/codex-own" doctor
|
|
37
|
+
"$HOME/.local/bin/codex-own" version
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
可选:需要使用模型时,再配置 API Key 并启动。将只包含一行 DashScope API Key 的文件放到下载目录,命名为 codex-own-api-key.txt;文件在其他位置时修改路径。配置前退出应用,已有可用配置可跳过配置命令。
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
"$HOME/.local/bin/codex-own" configure --api-key-file "$HOME/Downloads/codex-own-api-key.txt" --yes
|
|
44
|
+
"$HOME/.local/bin/codex-own" launch
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
以后更新:先退出 codex-own,再更新 npm 管理工具和桌面程序。配置与对话保存在 ~/Library/Application Support/codex-own-independent。
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install --global --prefix "$HOME/.local" @petercjl/codex-own@latest
|
|
51
|
+
"$HOME/.local/bin/codex-own" update check
|
|
52
|
+
"$HOME/.local/bin/codex-own" update install --yes
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 在 Windows 上通过 PowerShell 安装和测试
|
|
56
|
+
|
|
57
|
+
适用于运行 x64 Node.js 的 Windows。先从 Node.js 官网安装 Windows x64 的 LTS 版本(要求 Node.js 22 或以上,包含 npm),再重新打开普通用户 PowerShell。建议预留 10 GB 空间,用于下载、解压和备份。
|
|
58
|
+
|
|
59
|
+
[Node.js 官方下载](https://nodejs.org/en/download)
|
|
60
|
+
|
|
61
|
+
安装验收只需完成前三步。无需 Agent、API Key 或模型登录。
|
|
62
|
+
|
|
63
|
+
第一步:检查环境并安装 npm 管理工具。使用 .cmd 入口,无需修改 PowerShell 脚本执行策略。逐段执行,每一步成功后再继续。
|
|
64
|
+
|
|
65
|
+
```powershell
|
|
66
|
+
node --version
|
|
67
|
+
node -p "process.arch"
|
|
68
|
+
npm.cmd --version
|
|
69
|
+
npm.cmd install --global @petercjl/codex-own@latest
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
process.arch 应输出 x64。第二步:解析命令路径并安装桌面程序。$codexOwn 只在当前窗口有效;打开新窗口后重新执行赋值行。
|
|
73
|
+
|
|
74
|
+
```powershell
|
|
75
|
+
$codexOwn = Join-Path (npm.cmd prefix --global) "codex-own.cmd"
|
|
76
|
+
& $codexOwn install --yes
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
管理工具和桌面资源全部从 registry.npmjs.org 下载。Windows 桌面下载量约 878 MiB,安装时显示分片编号、下载百分比和已接收大小,然后显示校验、解压、安装。看到“安装完成”和 ok: true 才表示完成。
|
|
80
|
+
|
|
81
|
+
若已有手动安装的 codex-own,先退出应用,将第二步的安装命令替换为以下接管命令;原安装会被备份。
|
|
82
|
+
|
|
83
|
+
```powershell
|
|
84
|
+
& $codexOwn install --adopt --yes
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
第三步:检查安装结果。doctor 中 installed 应有版本、lock 为 false,pendingTransactions 为空;开始菜单出现 codex-own。仅检查安装时,configured: false 是正常状态。
|
|
88
|
+
|
|
89
|
+
```powershell
|
|
90
|
+
& $codexOwn doctor
|
|
91
|
+
& $codexOwn version
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
程序目录:%LOCALAPPDATA%\Programs\codex-own\current。配置与对话目录:%LOCALAPPDATA%\codex-own-independent。二者独立于原版 Codex。
|
|
95
|
+
|
|
96
|
+
安装测试通过后即可停止;账号配置和模型对话属于后续使用测试。
|
|
97
|
+
|
|
98
|
+
可选:需要使用模型时,将只包含一行 DashScope API Key 的文件放到下载目录,命名为 codex-own-api-key.txt。配置前先退出应用,文件位置不同则修改路径;已有可用配置可跳过配置命令。
|
|
99
|
+
|
|
100
|
+
```powershell
|
|
101
|
+
& $codexOwn configure --api-key-file "$HOME\Downloads\codex-own-api-key.txt" --yes
|
|
102
|
+
& $codexOwn launch
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
以后更新:先退出 codex-own,再执行以下命令。更新保留独立配置和对话。
|
|
106
|
+
|
|
107
|
+
```powershell
|
|
108
|
+
npm.cmd install --global @petercjl/codex-own@latest
|
|
109
|
+
$codexOwn = Join-Path (npm.cmd prefix --global) "codex-own.cmd"
|
|
110
|
+
& $codexOwn update check
|
|
111
|
+
& $codexOwn update install --yes
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
下载失败:连续 30 秒无数据会重试当前分片,最多 3 次。出现 DOWNLOAD_FAILED 时检查 npm 网络连接,再执行同一安装命令;已校验分片会复用。下载期间按 Ctrl+C 可以取消。遇到 BUSY 时先确认是否有另一安装进程,保留错误信息,不直接删除锁文件。
|
|
115
|
+
|
|
116
|
+
npm 安装出现缓存 ENOENT 或缓存损坏提示时,先检查缓存,再重新安装 npm 工具:
|
|
117
|
+
|
|
118
|
+
```powershell
|
|
119
|
+
npm.cmd cache verify
|
|
120
|
+
npm.cmd install --global @petercjl/codex-own@latest
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
管理工具与桌面版本分别报告。API Key 和用户数据保存在 npm 包之外。更多命令见 `codex-own help`。
|
package/capabilities.json
CHANGED
|
@@ -20,5 +20,15 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dataPolicy": "independent external directories; preserve credentials and conversations",
|
|
22
22
|
"updatePolicy": "explicit update; pinned verified build; refuse running app; backup before switch; restore data snapshot for rollback",
|
|
23
|
-
"authentication": "npm install does not grant model access; existing independent credentials remain local"
|
|
23
|
+
"authentication": "npm install does not grant model access; existing independent credentials remain local",
|
|
24
|
+
"distribution": {
|
|
25
|
+
"runtimeHosts": [
|
|
26
|
+
"registry.npmjs.org"
|
|
27
|
+
],
|
|
28
|
+
"desktopAssets": "content-addressed npm payload prereleases; install latest manager only",
|
|
29
|
+
"cache": "verified platform parts retained for retries",
|
|
30
|
+
"progress": "stderr; final result JSON on stdout",
|
|
31
|
+
"idleTimeoutSeconds": 30,
|
|
32
|
+
"downloadAttempts": 3
|
|
33
|
+
}
|
|
24
34
|
}
|
package/lib/manager.mjs
CHANGED
|
@@ -4,8 +4,8 @@ import path from 'node:path';
|
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import crypto from 'node:crypto';
|
|
6
6
|
import {execFileSync, spawn} from 'node:child_process';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import {networkFetch, verifiedDownload, progress} from './network.mjs';
|
|
8
|
+
import {validateParts, assembleNpmAsset} from './npm-assets.mjs';
|
|
9
9
|
|
|
10
10
|
export const packageRoot = path.resolve(import.meta.dirname, '..');
|
|
11
11
|
export const pkg = JSON.parse(fs.readFileSync(path.join(packageRoot,'package.json'),'utf8'));
|
|
@@ -22,27 +22,31 @@ export function rejectLinks(p) {
|
|
|
22
22
|
let q=path.resolve(p); while(true){ if(fs.existsSync(q)&&fs.lstatSync(q).isSymbolicLink())fail('UNSAFE_PATH',`Symbolic link/reparse path: ${q}`); const parent=path.dirname(q);if(parent===q)break;q=parent; }
|
|
23
23
|
}
|
|
24
24
|
export function validateManifest(m, platform) {
|
|
25
|
-
if(m.schema
|
|
25
|
+
if(![1,2].includes(m.schema)||m.repository!=='petercjl/codex-own'||!/^\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(m.version))fail('INVALID_MANIFEST','Invalid release identity');
|
|
26
26
|
const a=m.platforms?.[platform];if(!a)fail('PLATFORM_UNAVAILABLE',`No verified build for ${platform} in ${m.version}`);
|
|
27
27
|
if(!/^[a-f0-9]{64}$/.test(a.sha256)||!Number.isSafeInteger(a.size)||a.size<=0)fail('INVALID_MANIFEST','Invalid archive checksum/size');
|
|
28
|
+
if(m.schema===2){validateParts(a);if(a.entry!==(platform==='darwin-arm64'?'codex-own.app':'app'))fail('INVALID_MANIFEST','Unexpected archive entry');return a;}
|
|
28
29
|
const expected=`https://github.com/petercjl/codex-own/releases/download/own-v${m.version}/`;
|
|
29
30
|
if(!a.url?.startsWith(expected)||/[?#]/.test(a.url))fail('INVALID_MANIFEST','Unexpected artifact origin');
|
|
30
31
|
if(a.entry!==(platform==='darwin-arm64'?'codex-own.app':'app'))fail('INVALID_MANIFEST','Unexpected archive entry');
|
|
31
32
|
return a;
|
|
32
33
|
}
|
|
33
34
|
export async function digestFile(p) { const h=crypto.createHash('sha256');for await(const b of fs.createReadStream(p))h.update(b);return h.digest('hex'); }
|
|
34
|
-
export
|
|
35
|
-
if(fs.existsSync(dest))fail('TARGET_EXISTS','Download target exists');
|
|
36
|
-
const r=await fetch(url,{signal:AbortSignal.timeout(1200000)}); if(!r.ok)fail('DOWNLOAD_FAILED',`HTTP ${r.status}`);
|
|
37
|
-
await pipeline(Readable.fromWeb(r.body),fs.createWriteStream(dest,{flags:'wx',mode:0o600}));
|
|
38
|
-
if(fs.statSync(dest).size!==expected.size||await digestFile(dest)!==expected.sha256)fail('CHECKSUM_MISMATCH','Archive verification failed; installed app is unchanged');
|
|
39
|
-
}
|
|
35
|
+
export const download = verifiedDownload;
|
|
40
36
|
export function checkEntries(names) {
|
|
41
37
|
for(const n of names){const p=n.replaceAll('\\','/');if(p.startsWith('/')||/^[a-z]:/i.test(p)||p.split('/').includes('..'))fail('UNSAFE_ARCHIVE','Unsafe archive entry');}
|
|
42
38
|
}
|
|
39
|
+
export function tarExecutable(platform=process.platform,env=process.env,exists=fs.existsSync) {
|
|
40
|
+
if(platform!=='win32')return 'tar';
|
|
41
|
+
const root=env.SystemRoot||env.SYSTEMROOT||env.windir||env.WINDIR;
|
|
42
|
+
if(!root)fail('DEPENDENCY_MISSING','Windows system directory is unavailable');
|
|
43
|
+
const executable=path.win32.join(root,'System32','tar.exe');
|
|
44
|
+
if(!exists(executable))fail('DEPENDENCY_MISSING','Windows system tar.exe is required');
|
|
45
|
+
return executable;
|
|
46
|
+
}
|
|
43
47
|
export function unpack(archive,dest) {
|
|
44
|
-
const names=execFileSync(
|
|
45
|
-
fs.mkdirSync(dest);execFileSync(
|
|
48
|
+
const names=execFileSync(tarExecutable(),['-tf',archive],{encoding:'utf8',maxBuffer:30*1024*1024}).split('\n').filter(Boolean);checkEntries(names);
|
|
49
|
+
fs.mkdirSync(dest);execFileSync(tarExecutable(),['-xf',archive,'-C',dest],{stdio:'pipe'});
|
|
46
50
|
// Bundled framework links may be relative but must stay within the extracted tree.
|
|
47
51
|
function walk(p){for(const x of fs.readdirSync(p,{withFileTypes:true})){const f=path.join(p,x.name);if(x.isSymbolicLink()){const resolved=path.resolve(path.dirname(f),fs.readlinkSync(f));if(!resolved.startsWith(dest+path.sep))fail('UNSAFE_ARCHIVE','Escaping symlink');}else if(x.isDirectory())walk(f);}}
|
|
48
52
|
walk(dest);
|
|
@@ -84,8 +88,8 @@ export function switchApp({p,source,version,adopt=false,identity=appIdentity,isR
|
|
|
84
88
|
return {ok:true,version,target:p.target,backup:exists?old:null,dataBackup:exists?path.join(backup,'data'):null};
|
|
85
89
|
}
|
|
86
90
|
export async function locked(p,fn){rejectLinks(p.root);fs.mkdirSync(p.manager,{recursive:true,mode:0o700});const lock=path.join(p.manager,'operation.lock');let fd;try{fd=fs.openSync(lock,'wx',0o600);}catch{fail('BUSY','Another operation or interrupted lock exists; inspect doctor before recovery');}try{fs.writeSync(fd,JSON.stringify({pid:process.pid,time:new Date().toISOString()}));return await fn();}finally{fs.closeSync(fd);fs.unlinkSync(lock);}}
|
|
87
|
-
export async function latest() {const r=await
|
|
88
|
-
export async function manifestFor(v){if(v===pkg.version)return read(path.join(packageRoot,'release.json'));if(!/^\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(v))fail('INVALID_VERSION','Invalid version');const r=await
|
|
91
|
+
export async function latest() {const r=await networkFetch('https://registry.npmjs.org/@petercjl%2fcodex-own/latest',{signal:AbortSignal.timeout(30000)});if(!r.ok)fail('REGISTRY_UNAVAILABLE',`Registry HTTP ${r.status}`);const p=await r.json();if(p.name!==pkg.name)fail('INVALID_MANIFEST','Registry package mismatch');return p.version;}
|
|
92
|
+
export async function manifestFor(v){if(v===pkg.version)return read(path.join(packageRoot,'release.json'));if(!/^\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(v))fail('INVALID_VERSION','Invalid version');const r=await networkFetch(`https://registry.npmjs.org/@petercjl%2fcodex-own/${v}`,{signal:AbortSignal.timeout(30000)});if(!r.ok)fail('MANIFEST_UNAVAILABLE',`npm HTTP ${r.status}`);const p=await r.json(),m=p.ownDesktopManifest;if(p.name!==pkg.name||p.version!==v||m?.version!==v||m?.schema!==2)fail('MANIFEST_UNAVAILABLE','This version has no npm desktop manifest; update the npm manager to latest.');return m;}
|
|
89
93
|
export async function configure(keyFile){if(!keyFile)fail('KEY_FILE_REQUIRED','Provide --api-key-file');const key=fs.readFileSync(keyFile,'utf8').trim();if(!/^sk-[A-Za-z0-9_-]{10,}$/.test(key))fail('INVALID_KEY_FILE','Expected a single API key');const p=paths();return locked(p,async()=>{if(running(p).length)fail('APP_RUNNING','Quit codex-own first');rejectLinks(p.core);fs.mkdirSync(p.core,{recursive:true,mode:0o700});fs.chmodSync(p.core,0o700);
|
|
90
94
|
if(process.platform==='win32'){const sid=execFileSync('powershell.exe',['-NoProfile','-Command','[System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value'],{encoding:'utf8'}).trim();execFileSync('icacls.exe',[p.core,'/inheritance:r','/grant:r',`*${sid}:(OI)(CI)F`],{stdio:'pipe'});}
|
|
91
95
|
const auth=path.join(p.core,'auth.json');rejectLinks(auth);let backup=null;if(fs.existsSync(auth)){backup=auth+'.backup-'+crypto.randomUUID();fs.copyFileSync(auth,backup,fs.constants.COPYFILE_EXCL);fs.chmodSync(backup,0o600);}const tmp=auth+'.new-'+crypto.randomUUID();exclusive(tmp,{auth_mode:'apikey',OPENAI_API_KEY:key});fs.renameSync(tmp,auth);
|
|
@@ -93,18 +97,21 @@ export async function configure(keyFile){if(!keyFile)fail('KEY_FILE_REQUIRED','P
|
|
|
93
97
|
const config=path.join(p.core,'config.toml');if(!fs.existsSync(config))fs.writeFileSync(config,`model = "deepseek-v4.1-flash"\nmodel_provider = "dashscope_deepseek"\nforced_login_method = "api"\ncli_auth_credentials_store = "file"\nmodel_catalog_json = ${JSON.stringify(models.replaceAll('\\','/'))}\nmodel_reasoning_effort = "high"\nweb_search = "disabled"\n[model_providers.dashscope_deepseek]\nname = "DeepSeek-DashScope"\nbase_url = "https://dashscope.aliyuncs.com/compatible-mode/v1"\nwire_api = "responses"\nrequires_openai_auth = true\n`,{flag:'wx',mode:0o600});return {ok:true,configured:true,backup,configPreserved:true};});}
|
|
94
98
|
export async function install({version,adopt=false,archive,manifest}={}) {
|
|
95
99
|
const p=paths();if(!['darwin-arm64','win32-x64'].includes(p.platform))fail('UNSUPPORTED_PLATFORM',p.platform);
|
|
100
|
+
progress('准备安装:检查版本、平台与独立目录');
|
|
96
101
|
return locked(p,async()=>{const m=manifest||await manifestFor(version||pkg.version);const a=validateManifest(m,p.platform);
|
|
97
102
|
if(state(p)?.version===m.version&&!archive)return {ok:true,unchanged:true,version:m.version};
|
|
98
103
|
if(running(p).length)fail('APP_RUNNING','Quit codex-own before install/update');
|
|
99
104
|
const stage=fs.mkdtempSync(path.join(p.manager,'stage-'));let f=archive;
|
|
100
105
|
if(f){if(fs.statSync(f).size!==a.size||await digestFile(f)!==a.sha256)fail('CHECKSUM_MISMATCH','Local archive checksum mismatch');}
|
|
101
|
-
else{f=path.join(stage,'app.tar.gz');await
|
|
106
|
+
else{if(m.schema!==2)fail('NPM_ASSETS_REQUIRED','Update @petercjl/codex-own to the latest npm-only release.');f=path.join(stage,'app.tar.gz');const cache=path.join(p.manager,'npm-assets');rejectLinks(cache);await assembleNpmAsset(a,f,cache,tarExecutable());}
|
|
107
|
+
progress('校验通过,正在解压桌面程序');
|
|
102
108
|
const content=path.join(stage,'unpacked');unpack(f,content);const source=path.join(content,a.entry);
|
|
103
109
|
if(process.platform==='darwin')execFileSync('codesign',['--verify','--deep','--strict',source],{stdio:'pipe'});
|
|
110
|
+
progress('正在安装到独立目录');
|
|
104
111
|
const result=switchApp({p,source,version:m.version,adopt});
|
|
105
112
|
if(process.platform==='darwin')execFileSync('/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister',['-f',p.target],{stdio:'pipe'});
|
|
106
113
|
else shortcut(p);
|
|
107
|
-
fs.rmSync(stage,{recursive:true,force:true});return result;
|
|
114
|
+
fs.rmSync(stage,{recursive:true,force:true});progress('安装完成');return {...result,downloadSource:archive?'local-archive':'registry.npmjs.org'};
|
|
108
115
|
});
|
|
109
116
|
}
|
|
110
117
|
export function shortcut(p) {
|
|
@@ -118,4 +125,4 @@ export async function rollback({restoreData=false}={}){const p=paths();return lo
|
|
|
118
125
|
const preserved=path.join(path.dirname(p.target),'.codex-own-before-rollback-'+crypto.randomUUID());fs.renameSync(p.target,preserved);fs.renameSync(prev.app,p.target);
|
|
119
126
|
for(const n of ['core','desktop']){const snap=path.join(prev.data,n);if(fs.existsSync(p[n]))fs.renameSync(p[n],path.join(p.root,n+'.before-rollback-'+crypto.randomUUID()));if(fs.existsSync(snap))fs.cpSync(snap,p[n],{recursive:true,force:false,errorOnExist:true});}
|
|
120
127
|
writeState(p,{...(prev.state||{schema:1,identity:'local.codex-own',version:'adopted-legacy',target:p.target}),previous:null});return {ok:true,preserved,dataBackup:backup};});}
|
|
121
|
-
export function doctor(){const p=paths(),s=state(p);let tar=true;try{execFileSync(
|
|
128
|
+
export function doctor(){const p=paths(),s=state(p);let tar=true;try{execFileSync(tarExecutable(),['--version'],{stdio:'pipe'});}catch{tar=false;}const pending=fs.existsSync(p.manager)?fs.readdirSync(p.manager).filter(n=>/^transaction-.*\.json$/.test(n)&&!n.endsWith('.complete.json')&&!fs.existsSync(path.join(p.manager,n.replace('.json','.complete.json')))):[];return {ok:tar&&!pending.length,managerVersion:pkg.version,platform:p.platform,supported:['darwin-arm64','win32-x64'].includes(p.platform),installed:s?.version||null,paths:p,tar,pendingTransactions:pending,lock:fs.existsSync(path.join(p.manager,'operation.lock')),configured:fs.existsSync(path.join(p.core,'auth.json')),windowsGuiTested:false};}
|
package/lib/network.mjs
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import crypto from 'node:crypto';
|
|
3
|
+
import {EnvHttpProxyAgent, fetch} from 'undici';
|
|
4
|
+
|
|
5
|
+
const dispatcher = new EnvHttpProxyAgent();
|
|
6
|
+
export const progress = message => process.stderr.write(`[codex-own] ${message}\n`);
|
|
7
|
+
export function networkFetch(url, options = {}) {
|
|
8
|
+
return fetch(url, {dispatcher, redirect: 'error', ...options});
|
|
9
|
+
}
|
|
10
|
+
export async function fileHash(file) {
|
|
11
|
+
const h = crypto.createHash('sha256');
|
|
12
|
+
for await (const b of fs.createReadStream(file)) h.update(b);
|
|
13
|
+
return h.digest('hex');
|
|
14
|
+
}
|
|
15
|
+
export async function verifiedDownload(url, dest, expected, {
|
|
16
|
+
fetcher = networkFetch, report = progress, idleMs = 30000, attempts = 3, retryMs = 1000,
|
|
17
|
+
} = {}) {
|
|
18
|
+
const error = (code, message) => Object.assign(new Error(message), {code});
|
|
19
|
+
if (fs.existsSync(dest)) throw error('TARGET_EXISTS', 'Download destination already exists');
|
|
20
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
21
|
+
const controller = new AbortController();
|
|
22
|
+
let file, bytes = 0, timer, cancelled = false;
|
|
23
|
+
const start = Date.now();
|
|
24
|
+
const cancel = () => { cancelled = true; controller.abort(); };
|
|
25
|
+
const reset = () => { clearTimeout(timer); timer = setTimeout(() => controller.abort(), idleMs); };
|
|
26
|
+
const tick = () => report(`下载 ${Math.floor(bytes / expected.size * 100)}% · ${(bytes / 1048576).toFixed(1)} / ${(expected.size / 1048576).toFixed(1)} MiB · ${((Date.now()-start)/1000).toFixed(0)}s`);
|
|
27
|
+
process.once('SIGINT', cancel);
|
|
28
|
+
process.once('SIGTERM', cancel);
|
|
29
|
+
const interval = setInterval(tick, 3000);
|
|
30
|
+
try {
|
|
31
|
+
report(`连接 npm 下载服务(尝试 ${attempt}/${attempts})`);
|
|
32
|
+
reset();
|
|
33
|
+
const response = await fetcher(url, {signal: controller.signal});
|
|
34
|
+
if (!response.ok) { await response.body?.cancel(); throw error('DOWNLOAD_HTTP', `npm HTTP ${response.status}`); }
|
|
35
|
+
file = await fs.promises.open(dest, 'wx', 0o600);
|
|
36
|
+
const h = crypto.createHash('sha256');
|
|
37
|
+
for await (const data of response.body) {
|
|
38
|
+
reset(); bytes += data.length;
|
|
39
|
+
if (bytes > expected.size) throw error('CHECKSUM_MISMATCH', 'Download exceeded expected size');
|
|
40
|
+
h.update(data);
|
|
41
|
+
let offset = 0;
|
|
42
|
+
while (offset < data.length) offset += (await file.write(data, offset, data.length-offset)).bytesWritten;
|
|
43
|
+
}
|
|
44
|
+
if (bytes !== expected.size || h.digest('hex') !== expected.sha256) throw error('CHECKSUM_MISMATCH', 'Download verification failed');
|
|
45
|
+
tick();
|
|
46
|
+
return;
|
|
47
|
+
} catch (e) {
|
|
48
|
+
controller.abort();
|
|
49
|
+
if (file) { await file.close(); file = null; fs.unlinkSync(dest); }
|
|
50
|
+
if (cancelled) throw error('CANCELLED', 'Download cancelled. Verified earlier parts are retained; rerun the install command to continue.');
|
|
51
|
+
if (e.code === 'CHECKSUM_MISMATCH' || e.code === 'EEXIST') throw e;
|
|
52
|
+
if (attempt === attempts) throw error('DOWNLOAD_FAILED', `npm download failed after ${attempts} attempts (${e.code === 'DOWNLOAD_HTTP' ? e.message : e.code || e.cause?.code || 'timeout/network'}). Check network access to registry.npmjs.org and retry; verified parts are retained.`);
|
|
53
|
+
report(`下载暂不可用(${e.code === 'DOWNLOAD_HTTP' ? e.message : e.code || e.cause?.code || 'timeout/network'}),稍后重试当前分片;已校验分片保留。`);
|
|
54
|
+
await new Promise(r=>setTimeout(r,retryMs*attempt));
|
|
55
|
+
if (cancelled) throw error('CANCELLED', 'Download cancelled. Verified earlier parts are retained; rerun the install command to continue.');
|
|
56
|
+
} finally {
|
|
57
|
+
clearTimeout(timer); clearInterval(interval);
|
|
58
|
+
process.removeListener('SIGINT', cancel); process.removeListener('SIGTERM', cancel);
|
|
59
|
+
if (file) await file.close();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import {execFileSync} from 'node:child_process';
|
|
4
|
+
import {pipeline} from 'node:stream/promises';
|
|
5
|
+
import {fileHash, verifiedDownload, progress} from './network.mjs';
|
|
6
|
+
|
|
7
|
+
export const payloadVersion = sha => `0.0.0-payload.h${sha}`;
|
|
8
|
+
export const payloadURL = sha => `https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-${payloadVersion(sha)}.tgz`;
|
|
9
|
+
export function validateParts(asset) {
|
|
10
|
+
const fail = () => { throw Object.assign(new Error('Invalid npm asset manifest'), {code:'INVALID_MANIFEST'}); };
|
|
11
|
+
if (!Array.isArray(asset.parts) || asset.parts.length < 1 || asset.parts.length > 64) fail();
|
|
12
|
+
let size = 0;
|
|
13
|
+
for (const p of asset.parts) {
|
|
14
|
+
if (!/^[a-f0-9]{64}$/.test(p.sha256) || !/^[a-f0-9]{64}$/.test(p.archiveSha256)
|
|
15
|
+
|| !Number.isSafeInteger(p.size) || p.size < 1 || p.size > 128*1048576
|
|
16
|
+
|| !Number.isSafeInteger(p.archiveSize) || p.archiveSize < 1 || p.archiveSize > 130*1048576
|
|
17
|
+
|| p.url !== payloadURL(p.sha256)) fail();
|
|
18
|
+
size += p.size;
|
|
19
|
+
}
|
|
20
|
+
if (size !== asset.size) fail();
|
|
21
|
+
return asset;
|
|
22
|
+
}
|
|
23
|
+
function safeCache(file) {
|
|
24
|
+
if (fs.existsSync(file) && (!fs.lstatSync(file).isFile() || fs.lstatSync(file).isSymbolicLink()))
|
|
25
|
+
throw Object.assign(new Error('Unexpected npm asset cache entry'), {code:'UNSAFE_PATH'});
|
|
26
|
+
}
|
|
27
|
+
export async function assembleNpmAsset(asset, dest, cache, tar, {download = verifiedDownload, report = progress} = {}) {
|
|
28
|
+
validateParts(asset);
|
|
29
|
+
fs.mkdirSync(cache, {recursive:true, mode:0o700});
|
|
30
|
+
const files = [];
|
|
31
|
+
for (const [i, part] of asset.parts.entries()) {
|
|
32
|
+
report(`npm 资源分片 ${i+1}/${asset.parts.length}`);
|
|
33
|
+
const file = path.join(cache, `${part.sha256}.bin`);
|
|
34
|
+
safeCache(file);
|
|
35
|
+
if (fs.existsSync(file) && fs.statSync(file).size === part.size && await fileHash(file) === part.sha256) {
|
|
36
|
+
report('复用已校验分片'); files.push(file); continue;
|
|
37
|
+
}
|
|
38
|
+
if (fs.existsSync(file)) fs.unlinkSync(file);
|
|
39
|
+
const scratch = fs.mkdtempSync(path.join(cache, 'fetch-'));
|
|
40
|
+
try {
|
|
41
|
+
const archive = path.join(scratch, 'package.tgz');
|
|
42
|
+
await download(part.url, archive, {size:part.archiveSize, sha256:part.archiveSha256});
|
|
43
|
+
// Extract only the fixed payload entry, never arbitrary paths from a package tarball.
|
|
44
|
+
execFileSync(tar, ['-xf', archive, '-C', scratch, 'package/payload.bin'], {stdio:'pipe'});
|
|
45
|
+
const payload = path.join(scratch, 'package', 'payload.bin');
|
|
46
|
+
safeCache(payload);
|
|
47
|
+
if (fs.statSync(payload).size !== part.size || await fileHash(payload) !== part.sha256)
|
|
48
|
+
throw Object.assign(new Error('npm payload checksum mismatch'), {code:'CHECKSUM_MISMATCH'});
|
|
49
|
+
fs.renameSync(payload, file); files.push(file);
|
|
50
|
+
} finally { fs.rmSync(scratch, {recursive:true,force:true}); }
|
|
51
|
+
}
|
|
52
|
+
report('合并并校验完整桌面安装包');
|
|
53
|
+
const output = fs.createWriteStream(dest, {flags:'wx',mode:0o600});
|
|
54
|
+
async function* chunks() { for (const f of files) yield* fs.createReadStream(f); }
|
|
55
|
+
await pipeline(chunks(), output);
|
|
56
|
+
if (fs.statSync(dest).size !== asset.size || await fileHash(dest) !== asset.sha256)
|
|
57
|
+
throw Object.assign(new Error('Assembled desktop checksum mismatch'), {code:'CHECKSUM_MISMATCH'});
|
|
58
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@petercjl/codex-own",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Independent codex-own desktop installation and update manager",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -37,5 +37,142 @@
|
|
|
37
37
|
"pe-library": "1.0.1",
|
|
38
38
|
"plist": "3.1.0",
|
|
39
39
|
"resedit": "3.0.2"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"undici": "7.30.0"
|
|
43
|
+
},
|
|
44
|
+
"ownDesktopManifest": {
|
|
45
|
+
"schema": 2,
|
|
46
|
+
"repository": "petercjl/codex-own",
|
|
47
|
+
"version": "0.2.0",
|
|
48
|
+
"platforms": {
|
|
49
|
+
"darwin-arm64": {
|
|
50
|
+
"entry": "codex-own.app",
|
|
51
|
+
"size": 781692456,
|
|
52
|
+
"sha256": "6512d6d2e8ef46f64339ca1233fe833a067329cf006b17b3a0aa1bd27fd8e457",
|
|
53
|
+
"parts": [
|
|
54
|
+
{
|
|
55
|
+
"sha256": "c7fe9284f617e60fdebbedc2b551d59a021f3c03249e11224d15720ce90d968e",
|
|
56
|
+
"size": 134217728,
|
|
57
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.hc7fe9284f617e60fdebbedc2b551d59a021f3c03249e11224d15720ce90d968e.tgz",
|
|
58
|
+
"archiveSize": 134068421,
|
|
59
|
+
"archiveSha256": "7692f1071c0ed3a3c1937e00824815dfd50710a1bf16ff45b7ef54e3fc63a7da"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"sha256": "7ff2d11fbccf0d6f53b46eb0f62f12a399440640b4443a7770dab0e2aed07c86",
|
|
63
|
+
"size": 134217728,
|
|
64
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.h7ff2d11fbccf0d6f53b46eb0f62f12a399440640b4443a7770dab0e2aed07c86.tgz",
|
|
65
|
+
"archiveSize": 134125467,
|
|
66
|
+
"archiveSha256": "dab67fa0d1e9ea1ce11414cc1f90175513e7cd433c530fb94bf3e02075da97aa"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"sha256": "e3bc7bf7a8857fd325ba5a6c328a8ec15701a87c623d08f2c66565736a8b2125",
|
|
70
|
+
"size": 134217728,
|
|
71
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.he3bc7bf7a8857fd325ba5a6c328a8ec15701a87c623d08f2c66565736a8b2125.tgz",
|
|
72
|
+
"archiveSize": 134200688,
|
|
73
|
+
"archiveSha256": "8dd17509eee96f46e48b9dca3ef798df2b78ca1ad8d63f4cf914a6e6cfdff587"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"sha256": "206c25d0e17fee0419b7d498b223e43a12a11a367fddef9ba7ceea5f8d031c4a",
|
|
77
|
+
"size": 134217728,
|
|
78
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.h206c25d0e17fee0419b7d498b223e43a12a11a367fddef9ba7ceea5f8d031c4a.tgz",
|
|
79
|
+
"archiveSize": 133964876,
|
|
80
|
+
"archiveSha256": "013d7c22e993e2375f2ebac83954bb66c6b9e1e58ea62fea80339b4262922b62"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"sha256": "1e91bfbc20b3140e1f5770aaa732dcda1242a254bcd30b5d7c5cd1f350bc55fc",
|
|
84
|
+
"size": 134217728,
|
|
85
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.h1e91bfbc20b3140e1f5770aaa732dcda1242a254bcd30b5d7c5cd1f350bc55fc.tgz",
|
|
86
|
+
"archiveSize": 133524640,
|
|
87
|
+
"archiveSha256": "ab6368325f52c53f437dfdf774a13d24bf82c7c1102b3b4e588004cd850f3411"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"sha256": "1ab437b67cd09e7d3551ca659f41e7bfd984e7c763023d3b0575fa33f8be892a",
|
|
91
|
+
"size": 110603816,
|
|
92
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.h1ab437b67cd09e7d3551ca659f41e7bfd984e7c763023d3b0575fa33f8be892a.tgz",
|
|
93
|
+
"archiveSize": 110416084,
|
|
94
|
+
"archiveSha256": "0e0f0c34bf12238e6b737c90369aa014a70d70250dba37ccf9ebffd4c5af96a0"
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
"win32-x64": {
|
|
99
|
+
"entry": "app",
|
|
100
|
+
"size": 920368892,
|
|
101
|
+
"sha256": "f110a1e7e0f7c1e184ccc8733674d00280ecae02762530b07ca3b7d2547a4259",
|
|
102
|
+
"parts": [
|
|
103
|
+
{
|
|
104
|
+
"sha256": "f10bf3a916546c9b8d334b9bed9da2688cf48629aaa677b6b1265721ef695f09",
|
|
105
|
+
"size": 134217728,
|
|
106
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.hf10bf3a916546c9b8d334b9bed9da2688cf48629aaa677b6b1265721ef695f09.tgz",
|
|
107
|
+
"archiveSize": 134146498,
|
|
108
|
+
"archiveSha256": "cca9d9844548e9ada4d332be0620a248d20460c62b549faa3cb27532b1360ac9"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"sha256": "1c4eb544a87dae8072fbf6cfced05aba0c128be51ee59c1ba8a29ee1e06c2bb3",
|
|
112
|
+
"size": 134217728,
|
|
113
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.h1c4eb544a87dae8072fbf6cfced05aba0c128be51ee59c1ba8a29ee1e06c2bb3.tgz",
|
|
114
|
+
"archiveSize": 134017345,
|
|
115
|
+
"archiveSha256": "be3663292e80e953f48f2d3b59d93ea6030f78b411eca2333ff66c9b623f3e81"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"sha256": "96515fb363ccdfd35cabccb0c6c80d7377c07a17d546cb3dcd0e9995abdc52e3",
|
|
119
|
+
"size": 134217728,
|
|
120
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.h96515fb363ccdfd35cabccb0c6c80d7377c07a17d546cb3dcd0e9995abdc52e3.tgz",
|
|
121
|
+
"archiveSize": 134217117,
|
|
122
|
+
"archiveSha256": "b992e7d853bc0ee32acc4416b670238362473e91bb0cd0bb49b57d7a1431da30"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"sha256": "c72e99cd0a9b99fc023bfc53da528b16fcff89d9d11aa8ffb7cffe163ce15ac2",
|
|
126
|
+
"size": 134217728,
|
|
127
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.hc72e99cd0a9b99fc023bfc53da528b16fcff89d9d11aa8ffb7cffe163ce15ac2.tgz",
|
|
128
|
+
"archiveSize": 134037874,
|
|
129
|
+
"archiveSha256": "b7f72914ae410ca59d0f23038765683d096564f9a982880eeaaa2612121e9d98"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"sha256": "4cdd4bb887ae5046cac17eb6b6f197f0fc74d8d6f6cc7548d74d45a099c71f00",
|
|
133
|
+
"size": 134217728,
|
|
134
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.h4cdd4bb887ae5046cac17eb6b6f197f0fc74d8d6f6cc7548d74d45a099c71f00.tgz",
|
|
135
|
+
"archiveSize": 133901929,
|
|
136
|
+
"archiveSha256": "51510b54827188f5957eb2814795e1d1fe9c5abb8cbe753715a87e3c666a5dc4"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"sha256": "27eb58e92a273dac2abfad90fb5493634993f3c5f60249ac0074fadbfe93a7a3",
|
|
140
|
+
"size": 134217728,
|
|
141
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.h27eb58e92a273dac2abfad90fb5493634993f3c5f60249ac0074fadbfe93a7a3.tgz",
|
|
142
|
+
"archiveSize": 133616572,
|
|
143
|
+
"archiveSha256": "651d527dac76787fd19459804e1dac9957df897be44098f49085bb26035fa0de"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"sha256": "b38a0db220f90da06049e05516387bffdfa0f3b3fd2e9106bc2a04135d0b4779",
|
|
147
|
+
"size": 115062524,
|
|
148
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.hb38a0db220f90da06049e05516387bffdfa0f3b3fd2e9106bc2a04135d0b4779.tgz",
|
|
149
|
+
"archiveSize": 114529446,
|
|
150
|
+
"archiveSha256": "8cba55fc4c9b3592ef80601ba8f65881720c26de0b5d8ae4d589ebb6bb4771fe"
|
|
151
|
+
}
|
|
152
|
+
]
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
"provenance": {
|
|
156
|
+
"darwin-arm64": {
|
|
157
|
+
"darwin-arm64": {
|
|
158
|
+
"sourceRelease": "own-v0.1.0"
|
|
159
|
+
},
|
|
160
|
+
"win32-x64": {
|
|
161
|
+
"kind": "prepared-independent-build",
|
|
162
|
+
"desktopVersion": "26.1007.21434",
|
|
163
|
+
"guiAcceptance": "user-pending"
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"win32-x64": {
|
|
167
|
+
"darwin-arm64": {
|
|
168
|
+
"sourceRelease": "own-v0.1.0"
|
|
169
|
+
},
|
|
170
|
+
"win32-x64": {
|
|
171
|
+
"kind": "prepared-independent-build",
|
|
172
|
+
"desktopVersion": "26.1007.21434",
|
|
173
|
+
"guiAcceptance": "user-pending"
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
40
177
|
}
|
|
41
178
|
}
|
package/release.json
CHANGED
|
@@ -1,19 +1,134 @@
|
|
|
1
1
|
{
|
|
2
|
-
"schema":
|
|
2
|
+
"schema": 2,
|
|
3
3
|
"repository": "petercjl/codex-own",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"platforms": {
|
|
6
6
|
"darwin-arm64": {
|
|
7
7
|
"entry": "codex-own.app",
|
|
8
|
-
"
|
|
8
|
+
"size": 781692456,
|
|
9
9
|
"sha256": "6512d6d2e8ef46f64339ca1233fe833a067329cf006b17b3a0aa1bd27fd8e457",
|
|
10
|
-
"
|
|
10
|
+
"parts": [
|
|
11
|
+
{
|
|
12
|
+
"sha256": "c7fe9284f617e60fdebbedc2b551d59a021f3c03249e11224d15720ce90d968e",
|
|
13
|
+
"size": 134217728,
|
|
14
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.hc7fe9284f617e60fdebbedc2b551d59a021f3c03249e11224d15720ce90d968e.tgz",
|
|
15
|
+
"archiveSize": 134068421,
|
|
16
|
+
"archiveSha256": "7692f1071c0ed3a3c1937e00824815dfd50710a1bf16ff45b7ef54e3fc63a7da"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"sha256": "7ff2d11fbccf0d6f53b46eb0f62f12a399440640b4443a7770dab0e2aed07c86",
|
|
20
|
+
"size": 134217728,
|
|
21
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.h7ff2d11fbccf0d6f53b46eb0f62f12a399440640b4443a7770dab0e2aed07c86.tgz",
|
|
22
|
+
"archiveSize": 134125467,
|
|
23
|
+
"archiveSha256": "dab67fa0d1e9ea1ce11414cc1f90175513e7cd433c530fb94bf3e02075da97aa"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"sha256": "e3bc7bf7a8857fd325ba5a6c328a8ec15701a87c623d08f2c66565736a8b2125",
|
|
27
|
+
"size": 134217728,
|
|
28
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.he3bc7bf7a8857fd325ba5a6c328a8ec15701a87c623d08f2c66565736a8b2125.tgz",
|
|
29
|
+
"archiveSize": 134200688,
|
|
30
|
+
"archiveSha256": "8dd17509eee96f46e48b9dca3ef798df2b78ca1ad8d63f4cf914a6e6cfdff587"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"sha256": "206c25d0e17fee0419b7d498b223e43a12a11a367fddef9ba7ceea5f8d031c4a",
|
|
34
|
+
"size": 134217728,
|
|
35
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.h206c25d0e17fee0419b7d498b223e43a12a11a367fddef9ba7ceea5f8d031c4a.tgz",
|
|
36
|
+
"archiveSize": 133964876,
|
|
37
|
+
"archiveSha256": "013d7c22e993e2375f2ebac83954bb66c6b9e1e58ea62fea80339b4262922b62"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"sha256": "1e91bfbc20b3140e1f5770aaa732dcda1242a254bcd30b5d7c5cd1f350bc55fc",
|
|
41
|
+
"size": 134217728,
|
|
42
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.h1e91bfbc20b3140e1f5770aaa732dcda1242a254bcd30b5d7c5cd1f350bc55fc.tgz",
|
|
43
|
+
"archiveSize": 133524640,
|
|
44
|
+
"archiveSha256": "ab6368325f52c53f437dfdf774a13d24bf82c7c1102b3b4e588004cd850f3411"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"sha256": "1ab437b67cd09e7d3551ca659f41e7bfd984e7c763023d3b0575fa33f8be892a",
|
|
48
|
+
"size": 110603816,
|
|
49
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.h1ab437b67cd09e7d3551ca659f41e7bfd984e7c763023d3b0575fa33f8be892a.tgz",
|
|
50
|
+
"archiveSize": 110416084,
|
|
51
|
+
"archiveSha256": "0e0f0c34bf12238e6b737c90369aa014a70d70250dba37ccf9ebffd4c5af96a0"
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"win32-x64": {
|
|
56
|
+
"entry": "app",
|
|
57
|
+
"size": 920368892,
|
|
58
|
+
"sha256": "f110a1e7e0f7c1e184ccc8733674d00280ecae02762530b07ca3b7d2547a4259",
|
|
59
|
+
"parts": [
|
|
60
|
+
{
|
|
61
|
+
"sha256": "f10bf3a916546c9b8d334b9bed9da2688cf48629aaa677b6b1265721ef695f09",
|
|
62
|
+
"size": 134217728,
|
|
63
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.hf10bf3a916546c9b8d334b9bed9da2688cf48629aaa677b6b1265721ef695f09.tgz",
|
|
64
|
+
"archiveSize": 134146498,
|
|
65
|
+
"archiveSha256": "cca9d9844548e9ada4d332be0620a248d20460c62b549faa3cb27532b1360ac9"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"sha256": "1c4eb544a87dae8072fbf6cfced05aba0c128be51ee59c1ba8a29ee1e06c2bb3",
|
|
69
|
+
"size": 134217728,
|
|
70
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.h1c4eb544a87dae8072fbf6cfced05aba0c128be51ee59c1ba8a29ee1e06c2bb3.tgz",
|
|
71
|
+
"archiveSize": 134017345,
|
|
72
|
+
"archiveSha256": "be3663292e80e953f48f2d3b59d93ea6030f78b411eca2333ff66c9b623f3e81"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"sha256": "96515fb363ccdfd35cabccb0c6c80d7377c07a17d546cb3dcd0e9995abdc52e3",
|
|
76
|
+
"size": 134217728,
|
|
77
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.h96515fb363ccdfd35cabccb0c6c80d7377c07a17d546cb3dcd0e9995abdc52e3.tgz",
|
|
78
|
+
"archiveSize": 134217117,
|
|
79
|
+
"archiveSha256": "b992e7d853bc0ee32acc4416b670238362473e91bb0cd0bb49b57d7a1431da30"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"sha256": "c72e99cd0a9b99fc023bfc53da528b16fcff89d9d11aa8ffb7cffe163ce15ac2",
|
|
83
|
+
"size": 134217728,
|
|
84
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.hc72e99cd0a9b99fc023bfc53da528b16fcff89d9d11aa8ffb7cffe163ce15ac2.tgz",
|
|
85
|
+
"archiveSize": 134037874,
|
|
86
|
+
"archiveSha256": "b7f72914ae410ca59d0f23038765683d096564f9a982880eeaaa2612121e9d98"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"sha256": "4cdd4bb887ae5046cac17eb6b6f197f0fc74d8d6f6cc7548d74d45a099c71f00",
|
|
90
|
+
"size": 134217728,
|
|
91
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.h4cdd4bb887ae5046cac17eb6b6f197f0fc74d8d6f6cc7548d74d45a099c71f00.tgz",
|
|
92
|
+
"archiveSize": 133901929,
|
|
93
|
+
"archiveSha256": "51510b54827188f5957eb2814795e1d1fe9c5abb8cbe753715a87e3c666a5dc4"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"sha256": "27eb58e92a273dac2abfad90fb5493634993f3c5f60249ac0074fadbfe93a7a3",
|
|
97
|
+
"size": 134217728,
|
|
98
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.h27eb58e92a273dac2abfad90fb5493634993f3c5f60249ac0074fadbfe93a7a3.tgz",
|
|
99
|
+
"archiveSize": 133616572,
|
|
100
|
+
"archiveSha256": "651d527dac76787fd19459804e1dac9957df897be44098f49085bb26035fa0de"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"sha256": "b38a0db220f90da06049e05516387bffdfa0f3b3fd2e9106bc2a04135d0b4779",
|
|
104
|
+
"size": 115062524,
|
|
105
|
+
"url": "https://registry.npmjs.org/@petercjl/codex-own/-/codex-own-0.0.0-payload.hb38a0db220f90da06049e05516387bffdfa0f3b3fd2e9106bc2a04135d0b4779.tgz",
|
|
106
|
+
"archiveSize": 114529446,
|
|
107
|
+
"archiveSha256": "8cba55fc4c9b3592ef80601ba8f65881720c26de0b5d8ae4d589ebb6bb4771fe"
|
|
108
|
+
}
|
|
109
|
+
]
|
|
11
110
|
}
|
|
12
111
|
},
|
|
13
112
|
"provenance": {
|
|
14
113
|
"darwin-arm64": {
|
|
15
|
-
"
|
|
16
|
-
|
|
114
|
+
"darwin-arm64": {
|
|
115
|
+
"sourceRelease": "own-v0.1.0"
|
|
116
|
+
},
|
|
117
|
+
"win32-x64": {
|
|
118
|
+
"kind": "prepared-independent-build",
|
|
119
|
+
"desktopVersion": "26.1007.21434",
|
|
120
|
+
"guiAcceptance": "user-pending"
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
"win32-x64": {
|
|
124
|
+
"darwin-arm64": {
|
|
125
|
+
"sourceRelease": "own-v0.1.0"
|
|
126
|
+
},
|
|
127
|
+
"win32-x64": {
|
|
128
|
+
"kind": "prepared-independent-build",
|
|
129
|
+
"desktopVersion": "26.1007.21434",
|
|
130
|
+
"guiAcceptance": "user-pending"
|
|
131
|
+
}
|
|
17
132
|
}
|
|
18
133
|
}
|
|
19
|
-
}
|
|
134
|
+
}
|
|
@@ -7,7 +7,7 @@ description: Install, update, launch and diagnose the independent codex-own desk
|
|
|
7
7
|
|
|
8
8
|
Input: the requested install, update, diagnosis or recovery action, existing installation state, optional supplied API key file, and user authorization. Output: manager and desktop versions, verified installation paths, backup evidence and unresolved errors. Package installation alone does not configure model access.
|
|
9
9
|
|
|
10
|
-
Use the installed `codex-own` CLI. Resolve its current bundled instructions with `codex-own skill source --json`; probe `version`, `capabilities` and `doctor`. Requires Node.js 22+, tar, a supported desktop OS and
|
|
10
|
+
Use the installed `codex-own` CLI. Resolve its current bundled instructions with `codex-own skill source --json`; probe `version`, `capabilities` and `doctor`. Requires Node.js 22+, tar, a supported desktop OS and access to registry.npmjs.org. Install the latest manager from npm; it downloads only the current platform’s desktop payloads from npm. Internal payload prereleases are data, not CLI entry points. macOS ARM64 and Windows x64 are implementation targets; a diagnostic result does not prove Windows GUI compatibility.
|
|
11
11
|
|
|
12
12
|
## Main line
|
|
13
13
|
|
|
@@ -16,10 +16,13 @@ Use the installed `codex-own` CLI. Resolve its current bundled instructions with
|
|
|
16
16
|
3. For an authorized update, quit codex-own gracefully and run `codex-own update install --yes`. Do not terminate the original Codex. The manager and desktop versions are separate. Updating the npm manager alone does not update the desktop application.
|
|
17
17
|
4. Read `doctor` and `version`, retain the returned backup locations, then use `codex-own launch` when requested. Verify startup separately from installer success.
|
|
18
18
|
|
|
19
|
+
For installation-only acceptance, verify doctor, version, target and shortcut without configuring credentials or launching the GUI unless requested. Progress goes to stderr; final results remain JSON on stdout.
|
|
20
|
+
|
|
19
21
|
## Recovery and boundaries
|
|
20
22
|
|
|
21
23
|
- APP_RUNNING: request or perform authorized graceful exit, then return to step 3.
|
|
22
24
|
- CHECKSUM_MISMATCH, UNMANAGED_TARGET, UNSAFE_PATH, or incompatible patches: stop; preserve the current application.
|
|
25
|
+
- DOWNLOAD_FAILED: inspect npm registry connectivity and retry the same command; verified payload parts are reused. Ctrl+C during download cancels and releases the operation lock. Keep TLS verification enabled.
|
|
23
26
|
- BUSY or pending transactions: inspect the recorded transaction and process before recovery; do not blindly delete locks or replay an installation.
|
|
24
27
|
- Rollback requires `codex-own rollback --restore-data --yes`. Explain that this restores the matching old data snapshot; newer data is preserved separately. Obtain authorization covering this restoration, then verify state.
|
|
25
28
|
- Credentials and conversation data stay outside Git and npm. Never print API keys. Never modify original Codex directories or global model environment variables.
|