@gizai/cli 2.0.1 → 2.1.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/LICENSE +21 -0
- package/README.md +9 -178
- package/bin/giz.js +77 -0
- package/bin/install.sh +261 -0
- package/package.json +16 -46
- package/cli/codex-relay.mjs +0 -46
- package/cli/computer-identity.mjs +0 -76
- package/cli/engine.mjs +0 -58
- package/cli/giz.mjs +0 -1112
- package/cli/mcp-stdio-client.mjs +0 -81
- package/cli/media-capture.mjs +0 -321
- package/cli/update.mjs +0 -150
- package/mcp.json +0 -6
- package/plugin.json +0 -10
package/cli/codex-relay.mjs
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
// Runs ONLY inside the user's Computer. stdout is the authenticated gateway wire,
|
|
2
|
-
// never credentials or provider error bodies. No listener or separate credential store.
|
|
3
|
-
import { readFile } from 'node:fs/promises'
|
|
4
|
-
import { homedir } from 'node:os'
|
|
5
|
-
import { join } from 'node:path'
|
|
6
|
-
import { execFileSync } from 'node:child_process'
|
|
7
|
-
|
|
8
|
-
const emit = value => process.stdout.write(JSON.stringify(value) + '\n')
|
|
9
|
-
try {
|
|
10
|
-
const auth = JSON.parse(await readFile(join(process.env.CODEX_HOME || join(homedir(), '.codex'), 'auth.json'), 'utf8'))
|
|
11
|
-
const token = auth.tokens?.access_token
|
|
12
|
-
const account = auth.tokens?.account_id
|
|
13
|
-
if (auth.OPENAI_API_KEY || !token || !account) {
|
|
14
|
-
emit({ status: 401 }); process.exitCode = 1
|
|
15
|
-
} else {
|
|
16
|
-
const headers = { authorization: `Bearer ${token}`, 'chatgpt-account-id': account, 'content-type': 'application/json', originator: 'codex_cli_rs' }
|
|
17
|
-
const mode = process.argv[2]
|
|
18
|
-
if (mode === 'models') {
|
|
19
|
-
const version = execFileSync('codex', ['--version'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim().split(' ').at(-1)
|
|
20
|
-
const response = await fetch(`https://chatgpt.com/backend-api/codex/models?client_version=${encodeURIComponent(version)}`, { headers, signal: AbortSignal.timeout(30000) })
|
|
21
|
-
if (!response.ok) emit({ status: response.status })
|
|
22
|
-
else {
|
|
23
|
-
const payload = await response.json()
|
|
24
|
-
emit({ status: 200, models: (payload.models || []).filter(model => model.visibility !== 'hide').map(model => ({ id: model.slug, label: model.display_name || model.slug })).filter(model => typeof model.id === 'string' && model.id.length <= 256) })
|
|
25
|
-
}
|
|
26
|
-
} else if (mode === 'responses') {
|
|
27
|
-
let input = ''
|
|
28
|
-
for await (const chunk of process.stdin) input += chunk
|
|
29
|
-
const body = JSON.parse(input)
|
|
30
|
-
body.store = false
|
|
31
|
-
body.stream = true
|
|
32
|
-
body.instructions ??= ''
|
|
33
|
-
delete body.max_output_tokens
|
|
34
|
-
delete body.temperature
|
|
35
|
-
delete body.top_p
|
|
36
|
-
const response = await fetch('https://chatgpt.com/backend-api/codex/responses', { method: 'POST', headers, body: JSON.stringify(body), signal: AbortSignal.timeout(300000) })
|
|
37
|
-
emit({ status: response.status })
|
|
38
|
-
if (response.ok) {
|
|
39
|
-
for await (const chunk of response.body) emit({ chunk: Buffer.from(chunk).toString('base64') })
|
|
40
|
-
emit({ done: true })
|
|
41
|
-
}
|
|
42
|
-
} else emit({ status: 400 })
|
|
43
|
-
}
|
|
44
|
-
} catch {
|
|
45
|
-
emit({ status: 503 }); process.exitCode = 1
|
|
46
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { lstat, readFile } from 'node:fs/promises'
|
|
2
|
-
import { request } from 'node:http'
|
|
3
|
-
|
|
4
|
-
export const IDENTITY_SOCKET = '/opt/giz/identity/identity.sock'
|
|
5
|
-
|
|
6
|
-
// One-release bridge: the managed CLI overlay can arrive before the Region gateway migration.
|
|
7
|
-
export async function resolveIdentitySocket() {
|
|
8
|
-
try {
|
|
9
|
-
await lstat(IDENTITY_SOCKET)
|
|
10
|
-
return IDENTITY_SOCKET
|
|
11
|
-
} catch (error) {
|
|
12
|
-
if (error.code !== 'ENOENT') throw error
|
|
13
|
-
return '/run/giz/identity.sock'
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
let cached
|
|
17
|
-
let pending
|
|
18
|
-
|
|
19
|
-
// The release overlay owns this stamp; outside npm installations do not have it.
|
|
20
|
-
export async function managedRevision() {
|
|
21
|
-
return readFile(new URL('../REVISION', import.meta.url), 'utf8').catch(error => {
|
|
22
|
-
if (error.code !== 'ENOENT') throw error
|
|
23
|
-
})
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// The CLI never accepts a socket path from options, environment, or request data.
|
|
27
|
-
export async function computerIdentity(rejectedToken) {
|
|
28
|
-
if (cached?.access_token !== rejectedToken && cached?.expires_at > Date.now() + 60_000) return cached
|
|
29
|
-
pending ??= resolveIdentitySocket().then(socketPath => new Promise((resolve, reject) => {
|
|
30
|
-
const req = request({ socketPath, path: '/token', method: 'POST',
|
|
31
|
-
headers: { 'content-length': '0' } }, res => {
|
|
32
|
-
let body = ''
|
|
33
|
-
res.setEncoding('utf8')
|
|
34
|
-
res.on('data', chunk => {
|
|
35
|
-
body += chunk
|
|
36
|
-
if (body.length > 16_384) req.destroy(new Error('The Computer identity response is too large.'))
|
|
37
|
-
})
|
|
38
|
-
res.on('error', reject)
|
|
39
|
-
res.on('end', () => {
|
|
40
|
-
try {
|
|
41
|
-
const value = JSON.parse(body)
|
|
42
|
-
if (res.statusCode !== 200) throw new Error(value.error_description || value.message || 'The Computer identity is unavailable.')
|
|
43
|
-
const url = new URL(value.baseUrl)
|
|
44
|
-
if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password ||
|
|
45
|
-
url.pathname !== '/' || url.search || url.hash || !value.access_token ||
|
|
46
|
-
value.token_type !== 'Bearer' || Number(value.expires_in) <= 0 || Number(value.expires_in) > 900) {
|
|
47
|
-
throw new Error('The Computer identity response is invalid.')
|
|
48
|
-
}
|
|
49
|
-
cached = { access_token: value.access_token, baseUrl: url.origin,
|
|
50
|
-
expires_at: Date.now() + Number(value.expires_in) * 1000 }
|
|
51
|
-
resolve(cached)
|
|
52
|
-
} catch (error) { reject(error) }
|
|
53
|
-
})
|
|
54
|
-
})
|
|
55
|
-
req.on('error', async error => {
|
|
56
|
-
if (error.code !== 'ENOENT') return reject(error)
|
|
57
|
-
try {
|
|
58
|
-
await lstat(socketPath)
|
|
59
|
-
reject(new Error('Cannot reach the Computer identity service. Try again shortly.'))
|
|
60
|
-
} catch (lookupError) {
|
|
61
|
-
if (lookupError.code !== 'ENOENT') return reject(lookupError)
|
|
62
|
-
try {
|
|
63
|
-
if (await managedRevision() !== undefined) {
|
|
64
|
-
reject(new Error('Cannot verify the Computer identity. Try again shortly or contact the owner.'))
|
|
65
|
-
} else resolve(undefined)
|
|
66
|
-
} catch (error) { reject(error) }
|
|
67
|
-
}
|
|
68
|
-
})
|
|
69
|
-
req.setTimeout(10_000, () => req.destroy(new Error('The Computer identity response timed out.')))
|
|
70
|
-
req.end()
|
|
71
|
-
})).catch(error => {
|
|
72
|
-
error.hint = 'Check that the Computer is running and try again; if it is blocked, contact the owner or an organization admin.'
|
|
73
|
-
throw error
|
|
74
|
-
}).finally(() => { pending = undefined })
|
|
75
|
-
return pending
|
|
76
|
-
}
|
package/cli/engine.mjs
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { spawn, execFileSync } from 'node:child_process'
|
|
2
|
-
import { mkdir } from 'node:fs/promises'
|
|
3
|
-
import { randomUUID } from 'node:crypto'
|
|
4
|
-
import path from 'node:path'
|
|
5
|
-
|
|
6
|
-
/** Local Computer execution: official binary, existing permissions, isolated reviewable worktree. */
|
|
7
|
-
export async function runEngine(engine, instruction, directory = process.cwd(), progress = () => {}) {
|
|
8
|
-
if (engine !== 'claude' || typeof instruction !== 'string' || !instruction.trim()) throw new Error('Run: giz engine run claude "instruction" [--dir path]')
|
|
9
|
-
const git = (args, cwd = directory) => execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], maxBuffer: 8 * 1024 * 1024 })
|
|
10
|
-
const env = { ...process.env }
|
|
11
|
-
for (const key of ['ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN', 'ANTHROPIC_BASE_URL', 'CLAUDE_CODE_USE_BEDROCK', 'CLAUDE_CODE_USE_VERTEX', 'CLAUDE_CODE_USE_FOUNDRY']) delete env[key]
|
|
12
|
-
const status = spawn('claude', ['auth', 'status'], { env, stdio: ['ignore', 'pipe', 'ignore'] })
|
|
13
|
-
let login = ''
|
|
14
|
-
status.stdout.on('data', chunk => { login += chunk })
|
|
15
|
-
await new Promise((resolve, reject) => { status.once('error', reject); status.once('close', code => code === 0 ? resolve() : reject(new Error('Log in to Claude Code in Connections.'))) })
|
|
16
|
-
const account = JSON.parse(login)
|
|
17
|
-
if (!account.loggedIn || account.authMethod !== 'claude.ai') throw new Error('A Claude subscription login is required.')
|
|
18
|
-
const root = git(['rev-parse', '--show-toplevel']).trim()
|
|
19
|
-
const common = path.resolve(root, git(['rev-parse', '--git-common-dir']).trim())
|
|
20
|
-
const worktree = path.join(common, 'giz-engine-worktrees', randomUUID())
|
|
21
|
-
await mkdir(path.dirname(worktree), { recursive: true })
|
|
22
|
-
git(['worktree', 'add', '--detach', worktree, 'HEAD'])
|
|
23
|
-
progress({ status: 'running', engine, worktree })
|
|
24
|
-
const child = spawn('claude', ['-p', instruction, '--output-format', 'stream-json', '--verbose', '--permission-mode', 'acceptEdits'],
|
|
25
|
-
{ cwd: worktree, env, detached: true, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
26
|
-
let output = '', summary = '', stopped = false
|
|
27
|
-
const stop = () => { stopped = true; try { process.kill(-child.pid, 'SIGTERM') } catch {} }
|
|
28
|
-
process.once('SIGTERM', stop); process.once('SIGINT', stop)
|
|
29
|
-
child.stdout.on('data', chunk => {
|
|
30
|
-
output += chunk
|
|
31
|
-
let newline
|
|
32
|
-
while ((newline = output.indexOf('\n')) >= 0) {
|
|
33
|
-
const line = output.slice(0, newline); output = output.slice(newline + 1)
|
|
34
|
-
try {
|
|
35
|
-
const event = JSON.parse(line)
|
|
36
|
-
if (event.type === 'result') summary = String(event.result || '')
|
|
37
|
-
// Do not pass raw tool inputs, prompts or authorization data into the task card.
|
|
38
|
-
progress({ status: 'running', event: event.type, engine, worktree })
|
|
39
|
-
} catch {}
|
|
40
|
-
}
|
|
41
|
-
})
|
|
42
|
-
child.stderr.resume()
|
|
43
|
-
let exitCode
|
|
44
|
-
try {
|
|
45
|
-
exitCode = await new Promise((resolve, reject) => { child.once('error', reject); child.once('close', code => resolve(code)) })
|
|
46
|
-
} finally { process.removeListener('SIGTERM', stop); process.removeListener('SIGINT', stop) }
|
|
47
|
-
const tracked = git(['diff', '--name-only', 'HEAD'], worktree).trim().split('\n').filter(Boolean)
|
|
48
|
-
const created = git(['ls-files', '--others', '--exclude-standard'], worktree).trim().split('\n').filter(Boolean)
|
|
49
|
-
let diff = git(['diff', '--binary', 'HEAD'], worktree)
|
|
50
|
-
for (const file of created) {
|
|
51
|
-
try { diff += git(['diff', '--no-index', '--binary', '--', '/dev/null', file], worktree) }
|
|
52
|
-
catch (error) {
|
|
53
|
-
if (error.status !== 1) throw error
|
|
54
|
-
diff += String(error.stdout)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return { status: stopped ? 'canceled' : exitCode === 0 ? 'completed' : 'failed', engine, exitCode, worktree, files: [...new Set([...tracked, ...created])], diff, summary }
|
|
58
|
-
}
|