@gizai/cli 1.2.1 → 2.0.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,33 @@
1
1
  # Giz CLI
2
2
 
3
- Connect once in the browser, then use live Giz text and media models, Drive, and Computer from a terminal or AI agent.
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
+
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.
4
10
 
5
11
  ```bash
6
12
  npm install -g @gizai/cli
7
- giz auth login
8
- giz chat gpt-5-6-luna "Say hello in Korean"
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
9
23
  giz generate image "A paper observatory at blue hour"
24
+ giz credits # check your balance
25
+ giz --help # every command
10
26
  ```
11
27
 
12
- Discover a model's live inputs, upload local media, and run it:
28
+ ## Run any model
29
+
30
+ Discover a model's live inputs, upload local files, and run it:
13
31
 
14
32
  ```bash
15
33
  giz model list --type video-generation
@@ -19,104 +37,89 @@ giz model run ltx2-distilled \
19
37
  --upload image=./first-frame.png
20
38
  ```
21
39
 
22
- MCP for Claude Code and Codex (install the chosen client first):
40
+ 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.
41
+
42
+ ## Use Giz from Claude Code, Codex and other agents
43
+
44
+ Register the Giz MCP server with your agent (install the client first):
23
45
 
24
46
  ```bash
25
47
  giz auth login
26
- giz mcp add codex
27
48
  giz mcp add claude
28
- giz mcp check --json
49
+ giz mcp add codex
50
+ giz mcp check --json # verifies the MCP handshake and lists tool names
29
51
  ```
30
52
 
31
- These commands register `giz mcp serve` over stdio. It forwards the server's live
32
- MCP protocol and shares the CLI's rotating OAuth credentials. Tokens are not
33
- copied into Claude or Codex configuration. A custom `GIZ_CONFIG` path is preserved.
53
+ 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.
34
54
 
35
- For clients with their own OAuth session, use `giz mcp add claude --transport http`
36
- and sign in through `/mcp`, or `giz mcp add codex --transport http`. The remote
37
- endpoint also supports OAuth discovery and scoped API keys. A manually copied
38
- OAuth access token expires after one hour; updating CLI credentials does not
39
- update a static client header.
55
+ 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.
40
56
 
41
- For a headless Claude Code check, after login and registration:
57
+ ## Drive, Computers and Apps
42
58
 
43
59
  ```bash
44
- claude -p 'Use model_list to list one Giz image model.' --allowedTools mcp__giz__model_list
60
+ giz drive ls
61
+ giz drive read /notes/todo.md
62
+ giz computer list
63
+ giz computer exec --deadline-ms 120000 -- ls -la # up to 15 minutes; the remote exit code is preserved
64
+ giz app list
45
65
  ```
46
66
 
47
- `giz mcp check --json` checks the MCP handshake and lists actual tool names without
48
- running a model. Claude's `--verbose --output-format stream-json` also shows MCP
49
- connection state. If registration reports an existing `giz` entry, remove that
50
- entry with the client's `mcp remove` command before adding the desired transport.
51
- The exact client failure is returned on stderr.
67
+ 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.
68
+
69
+ Free Computers have a runtime limit; check your plan before starting long workflows. Signing in does not extend that runtime.
70
+
71
+ ### Inside a Giz Computer
72
+
73
+ `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.
52
74
 
53
- Agent Skill:
75
+ ## Plugins
54
76
 
55
77
  ```bash
56
- giz skill install
78
+ giz plugin search QUERY [--origin skill|mcp]
79
+ giz plugin install ID --query QUERY
80
+ giz plugin list
81
+ giz connection list # connected apps
57
82
  ```
58
83
 
59
- The CLI stores rotating OAuth credentials as unencrypted JSON in
60
- `~/.config/giz/credentials.json` with mode `0600` (owner access only), using atomic
61
- writes and serialized sign-in, refresh, and sign-out. Failed token revocation is
62
- reported before local credentials are removed. It never stores a Giz password.
63
- The sign-in callback binds a fresh loopback port; use the URL printed by the
64
- current login. Progress goes to stderr, leaving `--json` stdout machine-readable.
84
+ Also available: `plugin inspect`, `update`, `remove`, `enable`, `disable`, `publish` and `unpublish`.
65
85
 
66
- After updating from 1.0.1, run `giz auth login` again to consent to `giz.inference`.
67
- Refreshing an old grant cannot add this scope. `giz auth status --json` shows
68
- granted scopes; a missing scope error names the required permission.
86
+ ## Authentication and security
69
87
 
70
- `giz computer exec --deadline-ms 120000 -- COMMAND ARGS...` uses the bounded
71
- Computer command API (2 minutes by default, up to 15 minutes). The CLI preserves
72
- the remote command's exit code; `giz job wait` exits with status 1 for failed or
73
- cancelled jobs. JSON results remain available on stdout. Free Computers have a runtime limit; check the current
74
- Computer plan before starting long workflows. MCP authentication does not extend
75
- that runtime. Media jobs run independently and are tracked with `giz job get`.
88
+ - `giz auth login` uses browser OAuth with a loopback callback; the CLI never sees or stores your password.
89
+ - 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.
90
+ - `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`.
91
+ - 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.
92
+ - An OAuth access token copied by hand expires after one hour. Prefer `giz mcp add`, which refreshes it for you.
76
93
 
77
- Create a scoped API key with `giz api-key create "My app"`; its secret is shown once. Set `GIZ_TOKEN` only for ephemeral automation. Run `giz --help` for chat, models, Drive, Computer, job, upload, and raw tool commands.
94
+ ## Configuration
78
95
 
79
- Documentation: <https://www.giz.ai/mcp/>
96
+ | Variable | Purpose |
97
+ | --- | --- |
98
+ | `GIZ_TOKEN` | Use this API key or token instead of the stored login |
99
+ | `GIZ_CONFIG` | Path of the credentials file |
100
+ | `GIZ_NO_UPDATE_CHECK=1` | Turn off the update notice (or set `updates.mode` to `off` in `credentials.json`) |
80
101
 
81
- ### Managed Giz Computers
102
+ The update check is cached for up to 24 hours and prints only in interactive terminals.
82
103
 
83
- Giz Computers receive the same CLI source through the Region release overlay, at
84
- `/opt/giz/managed/apps/giz-cli`, with `/opt/giz/managed/bin/giz` first on PATH.
85
- No npm installation or browser login is needed inside a managed Computer:
104
+ ## Upgrading from 1.x
86
105
 
87
- ```sh
88
- giz auth status
89
- giz drive ls
90
- giz model list
91
- ```
106
+ Version 2.0 removes `giz capability` with no alias. Skills, MCP servers and Agents now install as one plugin.
107
+
108
+ | 1.x | 2.0 |
109
+ | --- | --- |
110
+ | `giz capability list` | `giz connection list` (connected apps), `giz plugin list` (installed plugins) |
111
+ | `giz capability search skill\|mcp QUERY` | `giz plugin search QUERY [--origin skill\|mcp]` |
112
+ | `giz capability install skill\|mcp ID --query QUERY` | `giz plugin install ID --query QUERY` |
113
+ | `giz capability update\|remove ID` | `giz plugin update\|remove NAME` |
92
114
 
93
- The Region owns `/opt/giz/identity/identity.sock` and exchanges the Computer identity for a
94
- 15-minute access token. For this release only, when the new socket path is absent,
95
- the CLI reads `/run/giz/identity.sock` to support an overlay arriving before its
96
- gateway update. No environment variable can override the socket path. The CLI
97
- caches it only in memory; it never saves a refresh
98
- token or API key for this identity. `giz auth status` displays
99
- `컴퓨터 신원으로 로그인됨 (소유자 <email>)`. A saved `giz auth login` takes priority;
100
- `giz auth logout` removes that user login and returns to the Computer identity.
101
- Shared Computers can access only their own Drive, model discovery/inference, and
102
- jobs started by that Computer. API key, billing, organization administration,
103
- destructive operations and external writes require a separate user authorization.
104
- The managed `giz --version` includes the Region source revision; npm installations
105
- continue to report the package version. Existing Computers receive overlay updates
106
- without recreation. The Region's normal release activation attaches identity sockets
107
- for existing instances, and socket replacement remains visible through the directory mount.
108
-
109
- App 변경은 `giz app list`에서 확인한 `revision`을 명시적으로 전달합니다.
110
- `run`, `publish`, `delete`에는 `--expected-revision REVISION`이 필요합니다.
111
- `create`/`save`의 `--input`에는 `id`와 `expectedRevision`을 넣습니다(새 앱은 0).
112
- 목록을 다시 읽어 개정 번호를 자동 대체하지 않으므로, 검토 뒤 다른 변경이 생기면 서버가 충돌을 거부합니다.
113
-
114
- 아래 표는 CLI 디스패처와 도구 item에서 생성합니다. 갱신 명령:
115
- `GIZ_UPDATE_CLI_SURFACE=1 pnpm --filter giz-app-giz test --runInBand --runTestsByPath server/external/cli-surface.spec.ts`.
116
- 일반 CI 실행은 문서를 쓰지 않고 표의 차이를 실패로 보고합니다.
115
+ 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.
116
+
117
+ ## Command reference
118
+
119
+ Generated from the CLI dispatcher and the Giz tool descriptors.
117
120
 
118
121
  <!-- cli-surface:start -->
119
- | CLI 명령 | 도구 ID | MCP 이름 | 필수 입력 | scope | Agent 발견 |
122
+ | Command | Tool ID | MCP tool | Required input | Scope | Agent discovery |
120
123
  | --- | --- | --- | --- | --- | --- |
121
124
  | model list | model.list | model_list | - | giz.read | model.list |
122
125
  | model get | model.get | model_get | model | giz.read | model.get |
@@ -130,6 +133,7 @@ App 변경은 `giz app list`에서 확인한 `revision`을 명시적으로 전
130
133
  | job get | job.get | job_get | job | giz.read | server job |
131
134
  | job wait | job.get | job_get | job | giz.read | server job |
132
135
  | job cancel | job.cancel | job_cancel | job | giz.destructive | server job |
136
+ | credits | account.credits | account_credits | - | giz.read | account.credits |
133
137
  | drive ls | drive.list | drive_list | - | giz.read | drive.list |
134
138
  | drive list | drive.list | drive_list | - | giz.read | drive.list |
135
139
  | drive search | drive.search | drive_search | query | giz.read | drive.search |
@@ -155,12 +159,26 @@ App 변경은 `giz app list`에서 확인한 `revision`을 명시적으로 전
155
159
  | app publish | app.publish | app_publish | id, expectedRevision, visibility | giz.write | app.publish |
156
160
  | app open | app.open | app_open | id | giz.read | app.open |
157
161
  | app delete | app.delete | app_delete | id, expectedRevision | giz.destructive | app.delete |
158
- | capability list | capability.status | capability_status | - | giz.read | capability.status |
159
- | capability search | capability.catalog | capability_catalog | kind, query | giz.read | capability.catalog |
160
- | capability install | capability.install | capability_install | id, kind, query | giz.execute | capability.install |
161
- | capability update | capability.manage | capability_manage | id, operation | giz.destructive | capability.manage |
162
- | capability remove | capability.manage | capability_manage | id, operation | giz.destructive | capability.manage |
162
+ | plugin list | plugin.catalog | plugin_catalog | - | giz.read | plugin.catalog |
163
+ | plugin search | plugin.catalog | plugin_catalog | - | giz.read | plugin.catalog |
164
+ | plugin inspect | plugin.catalog | plugin_catalog | - | giz.read | plugin.catalog |
165
+ | plugin install | plugin.install | plugin_install | - | giz.execute | plugin.install |
166
+ | plugin update | plugin.manage | plugin_manage | operation | giz.destructive | plugin.manage |
167
+ | plugin enable | plugin.manage | plugin_manage | operation | giz.destructive | plugin.manage |
168
+ | plugin disable | plugin.manage | plugin_manage | operation | giz.destructive | plugin.manage |
169
+ | plugin remove | plugin.manage | plugin_manage | operation | giz.destructive | plugin.manage |
170
+ | plugin publish | plugin.manage | plugin_manage | operation | giz.destructive | plugin.manage |
171
+ | plugin unpublish | plugin.manage | plugin_manage | operation | giz.destructive | plugin.manage |
172
+ | connection list | capability.status | capability_status | - | giz.read | capability.status |
163
173
  | agent start | agent.start | agent_start | agentId, prompt | giz.execute | external only |
164
174
  | agent status | agent.status | agent_status | turnId | giz.read | agent.status |
165
175
  | agent cancel | agent.cancel | agent_cancel | turnId | giz.execute | agent.cancel |
166
176
  <!-- cli-surface:end -->
177
+
178
+ ## Support
179
+
180
+ Questions and bug reports: [support@giz.ai](mailto:support@giz.ai) or the Support Forum at [giz.ai](https://www.giz.ai).
181
+
182
+ ## License
183
+
184
+ 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).
@@ -33,18 +33,18 @@ export async function computerIdentity(rejectedToken) {
33
33
  res.setEncoding('utf8')
34
34
  res.on('data', chunk => {
35
35
  body += chunk
36
- if (body.length > 16_384) req.destroy(new Error('컴퓨터 신원 응답이 너무 큽니다.'))
36
+ if (body.length > 16_384) req.destroy(new Error('The Computer identity response is too large.'))
37
37
  })
38
38
  res.on('error', reject)
39
39
  res.on('end', () => {
40
40
  try {
41
41
  const value = JSON.parse(body)
42
- if (res.statusCode !== 200) throw new Error(value.error_description || value.message || '컴퓨터 신원을 사용할 수 없습니다.')
42
+ if (res.statusCode !== 200) throw new Error(value.error_description || value.message || 'The Computer identity is unavailable.')
43
43
  const url = new URL(value.baseUrl)
44
44
  if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password ||
45
45
  url.pathname !== '/' || url.search || url.hash || !value.access_token ||
46
46
  value.token_type !== 'Bearer' || Number(value.expires_in) <= 0 || Number(value.expires_in) > 900) {
47
- throw new Error('컴퓨터 신원 응답이 올바르지 않습니다.')
47
+ throw new Error('The Computer identity response is invalid.')
48
48
  }
49
49
  cached = { access_token: value.access_token, baseUrl: url.origin,
50
50
  expires_at: Date.now() + Number(value.expires_in) * 1000 }
@@ -56,20 +56,20 @@ export async function computerIdentity(rejectedToken) {
56
56
  if (error.code !== 'ENOENT') return reject(error)
57
57
  try {
58
58
  await lstat(socketPath)
59
- reject(new Error('컴퓨터 신원 서비스에 연결할 수 없습니다. 잠시 후 다시 시도해 주세요.'))
59
+ reject(new Error('Cannot reach the Computer identity service. Try again shortly.'))
60
60
  } catch (lookupError) {
61
61
  if (lookupError.code !== 'ENOENT') return reject(lookupError)
62
62
  try {
63
63
  if (await managedRevision() !== undefined) {
64
- reject(new Error('컴퓨터 신원을 확인할 수 없습니다. 잠시 후 다시 시도하거나 소유자에게 문의해 주세요.'))
64
+ reject(new Error('Cannot verify the Computer identity. Try again shortly or contact the owner.'))
65
65
  } else resolve(undefined)
66
66
  } catch (error) { reject(error) }
67
67
  }
68
68
  })
69
- req.setTimeout(10_000, () => req.destroy(new Error('컴퓨터 신원 응답 시간이 초과되었습니다.')))
69
+ req.setTimeout(10_000, () => req.destroy(new Error('The Computer identity response timed out.')))
70
70
  req.end()
71
71
  })).catch(error => {
72
- error.hint = '컴퓨터 상태를 확인하고 다시 시도해 주세요. 차단되었다면 소유자 또는 조직 관리자에게 문의해 주세요.'
72
+ error.hint = 'Check that the Computer is running and try again; if it is blocked, contact the owner or an organization admin.'
73
73
  throw error
74
74
  }).finally(() => { pending = undefined })
75
75
  return pending
package/cli/giz.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { computerIdentity, managedRevision } from './computer-identity.mjs'
3
+ import { checkForUpdate, explicitUpdate, installSource, maybeNotify } from './update.mjs'
3
4
  import { createHash, randomBytes } from 'node:crypto'
4
5
  import { openAsBlob } from 'node:fs'
5
6
  import { mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises'
@@ -133,7 +134,7 @@ async function credentials(baseUrl, rejectedToken) {
133
134
  if (!config?.access_token) {
134
135
  const computer = await computerIdentity(rejectedToken)
135
136
  if (!computer) throw new CliError('Not authenticated.', 'Run: giz auth login')
136
- if (computer.baseUrl !== baseUrl) throw new CliError('컴퓨터 신원은 다른 서버에서 사용할 수 없습니다.', '사용자 계정으로 giz auth login을 실행해 주세요.')
137
+ if (computer.baseUrl !== baseUrl) throw new CliError('A Computer identity cannot be used with another server.', 'Run giz auth login with your user account.')
137
138
  return computer
138
139
  }
139
140
  if (String(config.baseUrl).replace(/\/+$/, '') !== baseUrl) {
@@ -190,10 +191,10 @@ async function jsonResponse(response, baseUrl) {
190
191
  if (response.status === 404 && (payload?.code === 'EXTERNAL_TOOL_UNAVAILABLE' ||
191
192
  (new URL(response.url).pathname.startsWith('/api/tools/') && /^Unknown Giz tool: /.test(message)))) {
192
193
  const compatibility = payload?.code === 'EXTERNAL_TOOL_UNAVAILABLE'
193
- ? message : '서버 버전은 확인할 수 없습니다(구버전 서버).'
194
+ ? message : 'The server version is unknown (older server).'
194
195
  throw new CliError(
195
- `이 서버는 요청한 명령을 지원하지 않습니다. CLI ${CLI_VERSION}. ${compatibility}`,
196
- `서버 주소(${baseUrl})와 지원 버전을 확인해 주세요. 지원하는 서버는 --base-url로 선택할 수 있습니다.`,
196
+ `This server does not support the requested command. CLI ${CLI_VERSION}. ${compatibility}`,
197
+ `Check the server address (${baseUrl}) and its supported version; choose a supporting server with --base-url.`,
197
198
  response.status
198
199
  )
199
200
  }
@@ -342,8 +343,9 @@ async function waitForCallback(expectedState) {
342
343
  }
343
344
 
344
345
  function openBrowser(url) {
345
- const command = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'cmd' : 'xdg-open'
346
- const args = process.platform === 'win32' ? ['/c', 'start', '', url] : [url]
346
+ // Not `cmd /c start`: cmd splits the URL at its first `&`, so Windows sign-in lost client_id and every later parameter.
347
+ const command = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'rundll32' : 'xdg-open'
348
+ const args = process.platform === 'win32' ? ['url.dll,FileProtocolHandler', url] : [url]
347
349
  const child = spawn(command, args, { detached: true, stdio: 'ignore' })
348
350
  child.on('error', () => {})
349
351
  child.unref()
@@ -404,11 +406,11 @@ async function applyUploads(baseUrl, input, values = []) {
404
406
  function appRevision(options) {
405
407
  const raw = options['expected-revision']
406
408
  if (raw === undefined || raw === '') {
407
- throw new CliError('개정 번호가 필요합니다.', 'giz app list에서 확인한 revision을 --expected-revision으로 전달해 주세요.')
409
+ throw new CliError('A revision is required.', 'Pass the revision from giz app list as --expected-revision.')
408
410
  }
409
411
  const revision = Number(raw)
410
412
  if (typeof raw !== 'string' || !/^\d+$/.test(raw) || !Number.isSafeInteger(revision) || revision < 1 || revision > 9_007_199_254_740_990) {
411
- throw new CliError('개정 번호는 1 이상의 안전한 정수여야 합니다.', 'giz app list에서 현재 revision을 확인해 주세요.')
413
+ throw new CliError('The revision must be a safe integer of 1 or more.', 'Check the current revision with giz app list.')
412
414
  }
413
415
  return revision
414
416
  }
@@ -446,11 +448,26 @@ async function serveMcp(baseUrl) {
446
448
 
447
449
  async function runCommand(baseUrl, positionals, options) {
448
450
  const [command, subcommand, ...rest] = positionals
451
+ if (command === '__update-check') {
452
+ await checkForUpdate({ file: options.cache, url: options.registry })
453
+ return undefined
454
+ }
455
+ if (command === 'update' && options.help === true) return { kind: 'help', value: help('update') }
456
+ if (command === 'update' && !subcommand) {
457
+ const result = await explicitUpdate({ version: CLI_VERSION, source: installSource() })
458
+ return { kind: 'message', value: result.message }
459
+ }
449
460
  if (options.version === true) {
450
461
  const revision = await managedRevision()
451
462
  return { kind: 'message', value: CLI_VERSION + (revision ? ` (region ${revision.trim()})` : '') }
452
463
  }
453
- if (!command || command === 'help' || options.help === true) return { kind: 'help', value: help(command) }
464
+ if (!command || command === 'help' || options.help === true) {
465
+ // A script that asks for help on a command that does not exist must fail, not read the overview as success.
466
+ if (command && command !== 'help' && !help().split('\n').some((line) => line.trim().split(' ')[1] === command && line.startsWith(' giz '))) {
467
+ throw new CliError(`Unknown command "${command}".`, 'Run: giz --help')
468
+ }
469
+ return { kind: 'help', value: help(command) }
470
+ }
454
471
  if (command === 'engine' && subcommand === 'run') {
455
472
  if (rest[0] !== 'claude') throw new CliError('Only Claude Code is an engine connection.')
456
473
  await api(baseUrl, '/api/external/engine/claude/authorization')
@@ -505,6 +522,11 @@ async function runCommand(baseUrl, positionals, options) {
505
522
  }
506
523
  }
507
524
 
525
+ if (command === 'credits') {
526
+ // Batch scripts read the balance before paid work instead of learning it from a refusal (MCP-TOOL-SCHEMA-LIMITS).
527
+ if (subcommand) throw new CliError(`Unknown command "credits ${subcommand}".`, 'Run: giz credits')
528
+ return { kind: 'result', value: await invoke(baseUrl, 'account.credits', {}) }
529
+ }
508
530
  if (command === 'model' && subcommand === 'list') {
509
531
  return {
510
532
  kind: 'models',
@@ -638,6 +660,11 @@ async function runCommand(baseUrl, positionals, options) {
638
660
  if (command === 'table' && subcommand === 'action') {
639
661
  const view = required(rest[0], 'TABLE', 'Run: giz table action TABLE ACTION [--id ID] --input JSON')
640
662
  const action = required(rest[1], 'ACTION', 'Run: giz table get TABLE to see its actions')
663
+ // Namespaced table tools use their canonical external contract and table ownership checks.
664
+ if (action.startsWith('table.')) {
665
+ if (options.id !== undefined) throw new CliError('Table tools do not take --id.', 'Put row IDs in --input.')
666
+ return { kind: 'result', value: await invoke(baseUrl, action, { ...jsonInput(options.input), table: view }) }
667
+ }
641
668
  const id = options.id === undefined ? undefined : String(options.id).includes(',') ? String(options.id).split(',') : String(options.id)
642
669
  return {
643
670
  kind: 'result',
@@ -716,7 +743,7 @@ async function runCommand(baseUrl, positionals, options) {
716
743
  throw new CliError(`Unknown app command "${subcommand || ''}".`, 'Run: giz app --help')
717
744
  }
718
745
 
719
- if (command === 'capability') {
746
+ if (command === 'connection') {
720
747
  if (subcommand === 'list') {
721
748
  return {
722
749
  kind: 'result',
@@ -725,44 +752,65 @@ async function runCommand(baseUrl, positionals, options) {
725
752
  })
726
753
  }
727
754
  }
728
- if (subcommand === 'search') {
729
- const kind = required(rest[0], 'KIND', 'Run: giz capability search skill|mcp QUERY')
730
- const query = required(rest.slice(1).join(' '), 'QUERY', 'Run: giz capability search skill|mcp QUERY')
755
+ throw new CliError(`Unknown connection command "${subcommand || ''}".`, 'Run: giz connection --help')
756
+ }
757
+
758
+ if (command === 'plugin') {
759
+ const computer = options.computer ? { computer: options.computer } : {}
760
+ if (subcommand === 'list' || subcommand === 'search') {
761
+ const query = subcommand === 'search'
762
+ ? required(rest.join(' '), 'QUERY', 'Run: giz plugin search QUERY [--origin catalog|skill|mcp]')
763
+ : undefined
731
764
  return {
732
765
  kind: 'result',
733
- value: await invoke(baseUrl, 'capability.catalog', {
734
- kind,
735
- query,
736
- ...(options.limit ? { limit: Number(options.limit) } : {})
766
+ value: await invoke(baseUrl, 'plugin.catalog', {
767
+ ...(query ? { query } : { origin: 'installed' }),
768
+ ...(query && options.origin ? { origin: options.origin } : {}),
769
+ ...(options.limit ? { limit: Number(options.limit) } : {}),
770
+ ...computer
737
771
  })
738
772
  }
739
773
  }
774
+ if (subcommand === 'inspect') {
775
+ const drivePath = required(rest[0], 'PATH', 'Run: giz plugin inspect DRIVE_PATH')
776
+ return { kind: 'result', value: await invoke(baseUrl, 'plugin.catalog', { path: drivePath, ...computer }) }
777
+ }
740
778
  if (subcommand === 'install') {
741
- const kind = required(rest[0], 'KIND', 'Run: giz capability install skill|mcp ID --query QUERY')
742
- const id = required(rest[1], 'ID', 'Run: giz capability install skill|mcp ID --query QUERY')
743
- const query = required(options.query, 'QUERY', 'Repeat the catalog query with --query QUERY.')
779
+ const source = options.path
780
+ ? { path: options.path }
781
+ : { id: required(rest[0], 'ID', 'Run: giz plugin install ID --query QUERY, or giz plugin install --path DRIVE_PATH') }
744
782
  return {
745
783
  kind: 'result',
746
- value: await invoke(baseUrl, 'capability.install', {
747
- kind,
748
- id,
749
- query,
750
- ...(options.computer ? { computer: options.computer } : {})
784
+ value: await invoke(baseUrl, 'plugin.install', {
785
+ ...source,
786
+ ...(options.query ? { query: options.query } : {}),
787
+ ...computer
751
788
  })
752
789
  }
753
790
  }
754
- if (subcommand === 'update' || subcommand === 'remove') {
755
- const id = required(rest[0], 'ID', `Run: giz capability ${subcommand} ID`)
791
+ if (['update', 'enable', 'disable', 'remove'].includes(subcommand)) {
792
+ const name = required(rest[0], 'NAME', `Run: giz plugin ${subcommand} NAME`)
793
+ return { kind: 'result', value: await invoke(baseUrl, 'plugin.manage', { operation: subcommand, name, ...computer }) }
794
+ }
795
+ if (subcommand === 'publish') {
796
+ const drivePath = required(rest[0], 'PATH', 'Run: giz plugin publish DRIVE_PATH --visibility organization|public')
797
+ const visibility = required(options.visibility, 'VISIBILITY', 'Add --visibility organization|public.')
756
798
  return {
757
799
  kind: 'result',
758
- value: await invoke(baseUrl, 'capability.manage', {
759
- id,
760
- operation: subcommand,
761
- ...(options.computer ? { computer: options.computer } : {})
800
+ value: await invoke(baseUrl, 'plugin.manage', {
801
+ operation: 'publish',
802
+ path: drivePath,
803
+ visibility,
804
+ ...(options.organization ? { organizationId: options.organization } : {}),
805
+ ...computer
762
806
  })
763
807
  }
764
808
  }
765
- throw new CliError(`Unknown capability command "${subcommand || ''}".`, 'Run: giz capability --help')
809
+ if (subcommand === 'unpublish') {
810
+ const releaseId = required(rest[0], 'RELEASE', 'Run: giz plugin unpublish RELEASE')
811
+ return { kind: 'result', value: await invoke(baseUrl, 'plugin.manage', { operation: 'unpublish', releaseId }) }
812
+ }
813
+ throw new CliError(`Unknown plugin command "${subcommand || ''}".`, 'Run: giz plugin --help')
766
814
  }
767
815
 
768
816
  if (command === 'agent') {
@@ -853,7 +901,7 @@ function print(kind, value, json, output = process.stdout) {
853
901
  return
854
902
  }
855
903
  if (kind === 'auth' && value.identity?.type === 'computer') {
856
- output.write(`컴퓨터 신원으로 로그인됨 (소유자 ${value.email || value.name})\n`)
904
+ output.write(`Signed in as a Computer identity (owner ${value.email || value.name})\n`)
857
905
  return
858
906
  }
859
907
  if (kind === 'models') {
@@ -897,6 +945,11 @@ Progress and the final diff, files and summary are returned. Interrupting stops
897
945
  giz api-key revoke KEY_ID
898
946
 
899
947
  The secret is shown only when the key is created.`
948
+ if (command === 'credits') return `Usage
949
+ giz credits
950
+
951
+ Prints your remaining credit (purchased and plan), when plan credit resets, and this month's spend and charges.
952
+ In a workspace it prints the workspace's monthly budget. Read it before paid batch work.`
900
953
  if (command === 'chat') return `Usage
901
954
  giz chat MODEL "your prompt"
902
955
  giz chat MODEL --input '{"messages":[{"role":"user","content":"Hello"}]}'`
@@ -939,7 +992,7 @@ Generation returns a job immediately; use giz job wait JOB_ID for its result.`
939
992
 
940
993
  TABLE is a table's Drive path (for example Sales/Customers.view.ts) or a business object name from giz table list
941
994
  (for example task, issue, requirement). giz table get shows its fields, the row changes it accepts (writes) and its
942
- actions; an object without a write changes only through its actions. Use --org to work in an organization.
995
+ actions. Namespaced table actions (such as table.updateSchema) use the table tool contract; an object without a write changes only through its actions. Use --org to work in an organization.
943
996
  read input: {"select":["name"],"filters":[{"field":"status","operator":"eq","value":"open"}],"limit":50}
944
997
  write input: {"create":[{"name":"Acme"}],"update":[{"id":"ROW_ID","status":"done"}],"remove":["ROW_ID"]}
945
998
  One write is one change people can undo in the table. At most 200 rows per write.`
@@ -954,11 +1007,19 @@ One write is one change people can undo in the table. At most 200 rows per write
954
1007
  giz app publish APP_ID private|workspace|public --expected-revision REVISION [--computer COMPUTER]
955
1008
  giz app open APP_ID [--computer COMPUTER]
956
1009
  giz app delete APP_ID --expected-revision REVISION [--computer COMPUTER]`
957
- if (command === 'capability') return `Usage
958
- giz capability list [--computer COMPUTER]
959
- giz capability search skill|mcp QUERY
960
- giz capability install skill|mcp ID --query QUERY [--computer COMPUTER]
961
- giz capability update|remove ID [--computer COMPUTER]`
1010
+ if (command === 'plugin') return `Usage
1011
+ giz plugin list [--computer COMPUTER]
1012
+ giz plugin search QUERY [--origin catalog|skill|mcp] [--limit N]
1013
+ giz plugin inspect DRIVE_PATH
1014
+ giz plugin install ID --query QUERY [--computer COMPUTER]
1015
+ giz plugin install --path DRIVE_PATH [--computer COMPUTER]
1016
+ giz plugin update|enable|disable|remove NAME [--computer COMPUTER]
1017
+ giz plugin publish DRIVE_PATH --visibility organization|public [--organization ORG]
1018
+ giz plugin unpublish RELEASE
1019
+
1020
+ A plugin (Agent Plugins 1.0) bundles skills, MCP servers and Agents; it is what you install, turn off and share.`
1021
+ if (command === 'connection') return `Usage
1022
+ giz connection list [--computer COMPUTER]`
962
1023
  if (command === 'agent') return `Usage
963
1024
  giz agent start [--computer COMPUTER] --input JSON
964
1025
  giz agent status TURN [--wait]
@@ -971,6 +1032,7 @@ One write is one change people can undo in the table. At most 200 rows per write
971
1032
 
972
1033
  stdio (default) shares giz auth login and refreshes tokens automatically.
973
1034
  http uses the client's own OAuth login. Install the chosen client first.`
1035
+ if (command === 'update') return `Usage\n giz update\n\nUpdates @gizai/cli with its owning package manager.`
974
1036
  if (command === 'auth') return `Usage
975
1037
  giz auth login [--device]
976
1038
  giz auth status
@@ -979,6 +1041,7 @@ http uses the client's own OAuth login. Install the chosen client first.`
979
1041
 
980
1042
  Usage
981
1043
  giz auth login [--device]
1044
+ giz update
982
1045
  giz api-key list|create|revoke
983
1046
  giz chat MODEL "your prompt"
984
1047
  giz model list [--type TYPE] [--search TEXT]
@@ -987,12 +1050,14 @@ Usage
987
1050
  giz generate image|video|music|talking-head "PROMPT" [--model MODEL]
988
1051
  giz generate audio --input JSON [--model MODEL]
989
1052
  giz job list|get|wait|cancel [JOB]
1053
+ giz credits
990
1054
  giz drive ls|list|search|read|write|mkdir|move|delete
991
1055
  giz table list|get|read|write|action
992
1056
  giz upload LOCAL_PATH
993
1057
  giz computer list|get|start|stop|exec
994
1058
  giz app list|create|save|run|publish|open|delete
995
- giz capability list|search|install|update|remove
1059
+ giz plugin list|search|inspect|install|update|enable|disable|remove|publish|unpublish
1060
+ giz connection list
996
1061
  giz agent start|status|cancel
997
1062
  giz engine run claude "instruction" [--dir path]
998
1063
  giz mcp url|serve|check|add
@@ -1012,9 +1077,17 @@ Docs: https://www.giz.ai/mcp/`
1012
1077
 
1013
1078
  async function main() {
1014
1079
  const { positionals, options } = parse(process.argv.slice(2))
1080
+ if (positionals[0] === '__update-check') {
1081
+ await runCommand('', positionals, options)
1082
+ return
1083
+ }
1084
+ const source = installSource()
1015
1085
  const saved = await readConfig()
1086
+ const updateMode = ['notify', 'off'].includes(saved?.updates?.mode) ? saved.updates.mode : 'notify'
1087
+ void maybeNotify({ version: CLI_VERSION, source, mode: updateMode, env: { ...process.env, GIZ_JSON: options.json === true ? '1' : process.env.GIZ_JSON } }).catch(() => {})
1016
1088
  const requiresAuth = positionals.length && !options.help && !options.version &&
1017
- !(positionals[0] === 'auth' && ['login', 'logout'].includes(positionals[1]))
1089
+ !(positionals[0] === 'auth' && ['login', 'logout'].includes(positionals[1])) &&
1090
+ !['update', '__update-check'].includes(positionals[0])
1018
1091
  const computer = requiresAuth && !process.env.GIZ_TOKEN && !saved?.access_token ? await computerIdentity() : undefined
1019
1092
  const baseUrl = String(options['base-url'] || process.env.GIZ_URL || saved?.baseUrl || computer?.baseUrl || DEFAULT_URL).replace(/\/+$/, '')
1020
1093
  if (typeof options.org === 'string') process.env.GIZ_ORGANIZATION = options.org