@gizai/cli 2.1.1 → 2.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 CHANGED
@@ -1,15 +1,196 @@
1
- # @gizai/cli
1
+ # Giz CLI
2
2
 
3
- GizStack 0.1.0-preview.20261023.5 from npm: natively compiled TypeScript, with the whole stack built in: a modern web framework and server, OLTP and OLAP SQL (PostgreSQL- and Oracle-compatible), vector search, key-value, distributed POSIX and S3 storage, GPU tensors, and local LLM, image and video generation. Docs: https://gizstack.com/docs/start
3
+ The official command-line interface for [giz.ai](https://www.giz.ai). Run Giz text, image, video and audio models, manage Drive files, tables, Apps and Computers, and connect Claude Code, Codex and other AI agents to your Giz account, all from a terminal.
4
4
 
5
- ```sh
5
+ [Documentation](https://www.giz.ai/mcp/) · [giz.ai](https://www.giz.ai) · [Sign up](https://www.giz.ai)
6
+
7
+ ## Install
8
+
9
+ Requires Node.js 20.12 or later and a [giz.ai](https://www.giz.ai) account.
10
+
11
+ ```bash
6
12
  npm install -g @gizai/cli
7
- npx @gizai/cli --version
8
- bunx @gizai/cli --version
13
+ giz --version
14
+ ```
15
+
16
+ Update later with `giz update` (or `npm install -g @gizai/cli`).
17
+
18
+ ## Quick start
19
+
20
+ ```bash
21
+ giz auth login # opens your browser, one-time sign-in
22
+ giz chat gpt-6-luna "Say hello in Korean" # chat with a text model
23
+ giz generate image "A paper observatory at blue hour"
24
+ giz credits # check your balance
25
+ giz --help # every command
26
+ ```
27
+
28
+ ## Gizlang
29
+
30
+ The same `giz` runs [Gizlang](https://gizstack.com/docs) and GizStack. The native toolchain is not part of this
31
+ package: the first Gizlang command installs it for your OS into `~/.giz` (`GIZ_INSTALL`), with its size shown first.
32
+
33
+ ```bash
34
+ giz init hello && cd hello # installs the toolchain once, then creates the project
35
+ giz run main.giz
36
+ giz deploy # signs in with the same giz auth login
37
+ gizdb serve --dir ~/gizdb-data --port 5432
38
+ ```
39
+
40
+ ## Run any model
41
+
42
+ Discover a model's live inputs, upload local files, and run it:
43
+
44
+ ```bash
45
+ giz model list --type video-generation
46
+ giz model get ltx2-distilled
47
+ giz model run ltx2-distilled \
48
+ --input '{"prompt":"A slow camera push through morning fog"}' \
49
+ --upload image=./first-frame.png
50
+ ```
51
+
52
+ Media jobs run independently. Track them with `giz job get JOB` or block with `giz job wait JOB` (exit status 1 if the job failed or was cancelled). Add `--json` to any command for machine-readable output on stdout; progress goes to stderr.
53
+
54
+ ## Use Giz from Claude Code, Codex and other agents
55
+
56
+ Register the Giz MCP server with your agent (install the client first):
57
+
58
+ ```bash
59
+ giz auth login
60
+ giz mcp add claude
61
+ giz mcp add codex
62
+ giz mcp check --json # verifies the MCP handshake and lists tool names
9
63
  ```
10
64
 
11
- One package serves every platform and runs no install script. The first run installs GizStack into `~/.giz`
12
- (`GIZ_INSTALL`): `bin/install.sh` finds your platform, downloads its installer from the content address
13
- written in the script, checks its SHA-256, and the installer checks the release signature and every file.
65
+ This registers `giz mcp serve` over stdio. It forwards Giz's live MCP protocol and shares the CLI's credentials, so no token is copied into the client's configuration. For clients that run their own OAuth session, use `giz mcp add claude --transport http` (then sign in with `/mcp`) or `giz mcp add codex --transport http`. The remote endpoint is `https://www.giz.ai/api/mcp`; it supports OAuth discovery and scoped API keys.
66
+
67
+ Install the Giz agent skill with `giz skill install`. The package root is also an [Agent Plugins](https://agent-plugins.org) 1.0 plugin (`plugin.json`, the `giz` skill and `mcp.json`), so a client that installs plugins gets the skill and the MCP server together.
68
+
69
+ ## Drive, Computers and Apps
70
+
71
+ ```bash
72
+ giz drive ls
73
+ giz drive read /notes/todo.md
74
+ giz computer list
75
+ giz computer exec --deadline-ms 120000 -- ls -la # up to 15 minutes; the remote exit code is preserved
76
+ giz app list
77
+ ```
78
+
79
+ App changes are explicit: pass the `revision` shown by `giz app list` as `--expected-revision REVISION` to `run`, `publish` and `delete`, and as `expectedRevision` in the `--input` of `create` and `save` (use 0 for a new App). A concurrent change is rejected by the server instead of being overwritten.
80
+
81
+ Free Computers have a runtime limit; check your plan before starting long workflows. Signing in does not extend that runtime.
82
+
83
+ ### Inside a Giz Computer
84
+
85
+ `giz` is already installed in every Giz Computer, and no login is needed: the Computer signs in as itself and can reach only its own Drive, model discovery and inference, and jobs it started. API keys, billing, organization administration, destructive operations and external writes need a separate user sign-in with `giz auth login`. `giz auth status` shows who you are.
86
+
87
+ ## Plugins
88
+
89
+ ```bash
90
+ giz plugin search QUERY [--origin skill|mcp]
91
+ giz plugin install ID --query QUERY
92
+ giz plugin list
93
+ giz connection list # connected apps
94
+ ```
95
+
96
+ Also available: `plugin inspect`, `update`, `remove`, `enable`, `disable`, `publish` and `unpublish`.
97
+
98
+ ## Authentication and security
99
+
100
+ - `giz auth login` uses browser OAuth with a loopback callback; the CLI never sees or stores your password.
101
+ - Credentials are stored as JSON in `~/.config/giz/credentials.json` with mode `0600` (owner only), written atomically. `GIZ_CONFIG` overrides the path. `giz auth logout` revokes the token and removes the file.
102
+ - `giz auth status --json` shows the granted scopes; a missing scope error names the permission it needs. After updating from 1.0.1, sign in again to grant `giz.inference`.
103
+ - Create a scoped API key with `giz api-key create "My app"` (the secret is shown once). Set `GIZ_TOKEN` only for short-lived automation.
104
+ - An OAuth access token copied by hand expires after one hour. Prefer `giz mcp add`, which refreshes it for you.
105
+
106
+ ## Configuration
107
+
108
+ | Variable | Purpose |
109
+ | --- | --- |
110
+ | `GIZ_TOKEN` | Use this API key or token instead of the stored login |
111
+ | `GIZ_CONFIG` | Path of the credentials file |
112
+ | `GIZ_NO_UPDATE_CHECK=1` | Turn off the update notice (or set `updates.mode` to `off` in `credentials.json`) |
113
+
114
+ The update check is cached for up to 24 hours and prints only in interactive terminals.
115
+
116
+ ## Upgrading from 1.x
117
+
118
+ Version 2.0 removes `giz capability` with no alias. Skills, MCP servers and Agents now install as one plugin.
119
+
120
+ | 1.x | 2.0 |
121
+ | --- | --- |
122
+ | `giz capability list` | `giz connection list` (connected apps), `giz plugin list` (installed plugins) |
123
+ | `giz capability search skill\|mcp QUERY` | `giz plugin search QUERY [--origin skill\|mcp]` |
124
+ | `giz capability install skill\|mcp ID --query QUERY` | `giz plugin install ID --query QUERY` |
125
+ | `giz capability update\|remove ID` | `giz plugin update\|remove NAME` |
126
+
127
+ New in 2.0: `plugin inspect`, `install --path`, `enable`, `disable`, `publish` and `unpublish`. A Giz server that supports plugins reports 2.0.0 as its minimum CLI, so 1.x prints an update warning there.
128
+
129
+ ## Command reference
130
+
131
+ Generated from the CLI dispatcher and the Giz tool descriptors.
132
+
133
+ <!-- cli-surface:start -->
134
+ | Command | Tool ID | MCP tool | Required input | Scope | Agent discovery |
135
+ | --- | --- | --- | --- | --- | --- |
136
+ | model list | model.list | model_list | - | giz.read | model.list |
137
+ | model get | model.get | model_get | model | giz.read | model.get |
138
+ | model run | model.run | model_run | model, input | giz.inference | model.run |
139
+ | generate image | generateImage | generateImage | prompt | giz.write | generateImage |
140
+ | generate video | generateVideo | generateVideo | prompt | giz.write | generateVideo |
141
+ | generate music | generateMusic | generateMusic | prompt | giz.write | generateMusic |
142
+ | generate talking-head | generateTalkingHead | generateTalkingHead | prompt, references | giz.write | generateTalkingHead |
143
+ | generate audio | generateAudio | generateAudio | purpose | giz.write | generateAudio |
144
+ | job list | job.list | job_list | - | giz.read | server job |
145
+ | job get | job.get | job_get | job | giz.read | server job |
146
+ | job wait | job.get | job_get | job | giz.read | server job |
147
+ | job cancel | job.cancel | job_cancel | job | giz.destructive | server job |
148
+ | credits | account.credits | account_credits | - | giz.read | account.credits |
149
+ | drive ls | drive.list | drive_list | - | giz.read | drive.list |
150
+ | drive list | drive.list | drive_list | - | giz.read | drive.list |
151
+ | drive search | drive.search | drive_search | query | giz.read | drive.search |
152
+ | drive read | drive.readFile | drive_readFile | path | giz.read | drive.readFile |
153
+ | drive write | drive.writeFile | drive_writeFile | path, content | giz.write | drive.writeFile |
154
+ | drive mkdir | drive.makeDirectory | drive_makeDirectory | path | giz.write | drive.makeDirectory |
155
+ | drive move | drive.movePath | drive_movePath | from, to | giz.write | drive.movePath |
156
+ | drive delete | drive.deletePath | drive_deletePath | path | giz.destructive | drive.deletePath |
157
+ | table list | workspace.objects | workspace_objects | - | giz.read | workspace.objects |
158
+ | table get | workspace.objects | workspace_objects | - | giz.read | workspace.objects |
159
+ | table read | table.readRows | table_readRows | table | giz.read | table.readRows |
160
+ | table write | table.writeRows | table_writeRows | table | giz.write | table.writeRows |
161
+ | table action | workspace.executeAction | workspace_executeAction | view, action | giz.execute | workspace.executeAction |
162
+ | computer list | computer.list | computer_list | - | giz.read | computer.list |
163
+ | computer get | computer.get | computer_get | - | giz.read | external only |
164
+ | computer start | computer.start | computer_start | - | giz.execute | external only |
165
+ | computer stop | computer.stop | computer_stop | - | giz.destructive | external only |
166
+ | computer exec | computer.exec | giz_computer_exec | - | giz.execute | computer.exec |
167
+ | app list | app.list | app_list | - | giz.read | app.list |
168
+ | app create | app.save | app_save | id, expectedRevision | giz.execute | app.save |
169
+ | app save | app.save | app_save | id, expectedRevision | giz.execute | app.save |
170
+ | app run | app.run | app_run | id, expectedRevision | giz.execute | app.run |
171
+ | app publish | app.publish | app_publish | id, expectedRevision, visibility | giz.write | app.publish |
172
+ | app open | app.open | app_open | id | giz.read | app.open |
173
+ | app delete | app.delete | app_delete | id, expectedRevision | giz.destructive | app.delete |
174
+ | plugin list | plugin.catalog | plugin_catalog | - | giz.read | plugin.catalog |
175
+ | plugin search | plugin.catalog | plugin_catalog | - | giz.read | plugin.catalog |
176
+ | plugin inspect | plugin.catalog | plugin_catalog | - | giz.read | plugin.catalog |
177
+ | plugin install | plugin.install | plugin_install | - | giz.execute | plugin.install |
178
+ | plugin update | plugin.manage | plugin_manage | operation | giz.destructive | plugin.manage |
179
+ | plugin enable | plugin.manage | plugin_manage | operation | giz.destructive | plugin.manage |
180
+ | plugin disable | plugin.manage | plugin_manage | operation | giz.destructive | plugin.manage |
181
+ | plugin remove | plugin.manage | plugin_manage | operation | giz.destructive | plugin.manage |
182
+ | plugin publish | plugin.manage | plugin_manage | operation | giz.destructive | plugin.manage |
183
+ | plugin unpublish | plugin.manage | plugin_manage | operation | giz.destructive | plugin.manage |
184
+ | connection list | capability.status | capability_status | - | giz.read | capability.status |
185
+ | agent start | agent.start | agent_start | agentId, prompt | giz.execute | external only |
186
+ | agent status | agent.status | agent_status | turnId | giz.read | agent.status |
187
+ | agent cancel | agent.cancel | agent_cancel | turnId | giz.execute | agent.cancel |
188
+ <!-- cli-surface:end -->
189
+
190
+ ## Support
191
+
192
+ Questions and bug reports: [support@giz.ai](mailto:support@giz.ai) or the Support Forum at [giz.ai](https://www.giz.ai).
193
+
194
+ ## License
14
195
 
15
- GizStack is open source under the MIT license: https://github.com/gizai/gizlang
196
+ Proprietary. Copyright Giz Inc. All rights reserved. Use of the service is governed by the [Terms of Use](https://www.giz.ai/terms-of-use).
@@ -0,0 +1,46 @@
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
+ }
@@ -0,0 +1,76 @@
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 ADDED
@@ -0,0 +1,58 @@
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
+ }