@tinycloud/cli 1.2.0-beta.1 → 1.2.0-beta.2
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 +10 -1
- package/dist/index.js +81 -9
- package/dist/index.js.map +1 -1
- package/dist/legacy-entry.js +763 -61
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +4 -4
- package/skills/tc-cli/AUTH.md +19 -0
- package/skills/tc-cli/REFERENCE.md +53 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tinycloud/cli",
|
|
3
|
-
"version": "1.2.0-beta.
|
|
3
|
+
"version": "1.2.0-beta.2",
|
|
4
4
|
"description": "TinyCloud CLI - manage data, delegations, and nodes from the terminal",
|
|
5
5
|
"author": "TinyCloud, Inc.",
|
|
6
6
|
"license": "EGPL",
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@tinycloud/share-sdk": "1.1.0",
|
|
33
33
|
"@tinycloud/share-envelope": "1.1.0",
|
|
34
|
-
"@tinycloud/node-sdk": "3.2.0-beta.
|
|
34
|
+
"@tinycloud/node-sdk": "3.2.0-beta.2",
|
|
35
35
|
"@tinycloud/node-sdk-wasm": "1.7.7",
|
|
36
|
-
"@tinycloud/sdk-core": "3.2.0-beta.
|
|
37
|
-
"@tinycloud/operations": "0.5.0-beta.
|
|
36
|
+
"@tinycloud/sdk-core": "3.2.0-beta.2",
|
|
37
|
+
"@tinycloud/operations": "0.5.0-beta.2",
|
|
38
38
|
"chalk": "^5.3.0",
|
|
39
39
|
"commander": "^14.0.0",
|
|
40
40
|
"open": "^11.0.0",
|
package/skills/tc-cli/AUTH.md
CHANGED
|
@@ -93,6 +93,25 @@ tc --profile agent-secrets secrets get OPENAI_API_KEY --raw
|
|
|
93
93
|
- A stored session that has expired, or no longer verifies for the profile's key, fails every command that needs it with `AUTH_REQUIRED` (exit 3) before any node request, whatever the profile kind; secrets commands on an owner OpenKey profile whose session has passed its expiry refresh it in the browser at a terminal instead. A delegate session created by `tc auth import` has no SIWE; its lifetime is the `exp` its compact-UCAN delegation signs, read from the stored authorization bytes, so it is refused the same way, including sessions imported by earlier releases. Expiry is judged by this machine's clock. `context` reports that expiry, and an expired imported session does not block the scoped paste login the hint names. A session imported from a CACAO (wallet-signed) delegation is still refused only by the node. The hint names a new scoped paste login for a delegate profile, `auth login --method local` for a local owner key, and `auth login --method openkey` for an OpenKey owner; Share commands keep their `auth login --device --manifest builtin:share-publishing` guidance. It is never reported as a retryable node error. Operations and MCP report it as `AUTH_REQUIRED` (`retryable: false`), the code they also use for a node's 401.
|
|
94
94
|
- `secrets get -o FILE` validates the destination and its existing directory before fetching or decrypting secret bytes. It refuses symlinks, directories and devices, syncs a new owner-only (0600) file, then atomically replaces the destination. Directory sync is attempted after replacement where supported; its failure does not report a failed write when the destination was already replaced. A destination that cannot be replaced fails with a sanitized filesystem error naming the destination; there is no unsafe fallback.
|
|
95
95
|
|
|
96
|
+
### Asking the owner to add a missing secret
|
|
97
|
+
|
|
98
|
+
When the secret does not exist yet, the agent sends the owner one message with a request link, redeems the consent short code if the owner approved a scoped login, then waits. Call the CLI by absolute path: a bare `tc` on `PATH` can be iproute2's `/usr/sbin/tc` (traffic control), not TinyCloud.
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
TC_BIN="$(npm prefix --global)/bin/tc"
|
|
102
|
+
"$TC_BIN" --profile agent-secrets secrets request OPENAI_API_KEY --requester "my-agent@$(hostname -s)" --json # offline; prints {url, names, owner}
|
|
103
|
+
# One message to the owner: the url (plus the scoped-login approval URL above if the profile has no grant yet).
|
|
104
|
+
# If the owner sends back a consent short code, redeem it first:
|
|
105
|
+
printf '%s\n' "$CODE" | "${LOGIN[@]}"
|
|
106
|
+
"$TC_BIN" --profile agent-secrets secrets wait OPENAI_API_KEY --timeout 9m --json # 0 ready, 8 still pending
|
|
107
|
+
"$TC_BIN" --profile agent-secrets secrets get OPENAI_API_KEY --raw
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
- `wait` never signs in: it needs a stored session and doesn't accept `TC_PRIVATE_KEY` (exit 3). Exit 3 carries `error.recovery.command`, the persistent login to run once.
|
|
111
|
+
- After a CLI upgrade, run one normal command per profile first (for example `"$TC_BIN" --profile agent-secrets auth caps`). Otherwise the first `wait` returns `PROFILE_MIGRATION_REQUIRED` (exit 1) once, with that command in `error.recovery.command`.
|
|
112
|
+
- Exit 8 means the owner has not added it yet: rerun `wait` without sending the link again. Exit 5 means the grant does not cover a name: `error.recovery.names` lists them. [REFERENCE.md](REFERENCE.md#asking-the-owner-for-a-secret-request-wait-manage) has the full exit table.
|
|
113
|
+
- A local identity (local-owner profile, a profile with a stored `privateKey`, `--private-key`, `TC_PRIVATE_KEY`) owns its secrets and gets `LOCAL_OWNER_PROFILE` from `request`: store them with `secrets put NAME --stdin`.
|
|
114
|
+
|
|
96
115
|
## More spaces after login
|
|
97
116
|
|
|
98
117
|
Login covers one space. Request further spaces on the logged-in profile:
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
<!-- BEGIN GENERATED TINYCloud operations coverage -->
|
|
4
4
|
This release has **Commander coverage tracked, not complete parity**:
|
|
5
5
|
|
|
6
|
-
-
|
|
6
|
+
- 2 migrated registration(s).
|
|
7
7
|
- 1 partially migrated registration(s).
|
|
8
|
-
-
|
|
8
|
+
- 127 legacy registration(s) remain Commander-owned.
|
|
9
9
|
|
|
10
10
|
- `auth import [source]` → `tinycloud.auth.import@1` (partial; legacy inputs: v1 delegation artifact, v1 permission artifact without command, bare portable delegation, stored delegation wrapper, cross-user delegation persisted with activated=false).
|
|
11
11
|
- `secrets get <name>` → `tinycloud.secrets.get@1` (migrated).
|
|
12
|
+
- `secrets probe <name>` → `tinycloud.secrets.status@1` (migrated).
|
|
12
13
|
<!-- END GENERATED TINYCloud operations coverage -->
|
|
13
14
|
|
|
14
15
|
## Calling the CLI
|
|
@@ -138,13 +139,60 @@ tc secrets network init
|
|
|
138
139
|
tc secrets network show
|
|
139
140
|
tc secrets network grant did:pkh:eip155:1:0xRecipient...
|
|
140
141
|
|
|
141
|
-
tc secrets put ANTHROPIC_API_KEY
|
|
142
|
+
printf '%s' "$ANTHROPIC_API_KEY" | tc secrets put ANTHROPIC_API_KEY --stdin # keeps the value out of argv and shell history
|
|
142
143
|
tc secrets get ANTHROPIC_API_KEY
|
|
143
144
|
tc secrets get ANTHROPIC_API_KEY -o key.txt # atomic owner-only (0600) replacement
|
|
144
145
|
tc secrets list
|
|
145
146
|
tc secrets delete ANTHROPIC_API_KEY
|
|
146
147
|
```
|
|
147
148
|
|
|
149
|
+
A missing secret makes `tc secrets get` exit 4 (`NOT_FOUND`). For an identity that is not local, `error.setup` carries the owner's request link; a local identity (or a name/scope no link can express) gets only a hint naming `tc secrets put NAME --stdin`. `tc secrets doctor NAME` hints `tc secrets request NAME` for a missing secret, or `tc secrets put NAME --stdin` for a local identity. `tc secrets get --delegation` follows the same rule, and a delegated read the node refuses (403, a capability 401, `AUTH_UNAUTHORIZED`) exits 5.
|
|
150
|
+
|
|
151
|
+
### Asking the owner for a secret: `request`, `wait`, `manage`
|
|
152
|
+
|
|
153
|
+
`/usr/sbin/tc` (iproute2 traffic control) can shadow TinyCloud's `tc` on `PATH`, so scripts call the CLI by absolute path:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
TC_BIN="$(npm prefix --global)/bin/tc"
|
|
157
|
+
"$TC_BIN" --profile agent secrets request OPENAI_API_KEY --requester "agent@host" --json
|
|
158
|
+
# {"url":"https://secrets.tinycloud.xyz/request?owner=…&name=OPENAI_API_KEY&requester=…&v=1","names":["OPENAI_API_KEY"],"owner":"did:pkh:eip155:1:0x…"}
|
|
159
|
+
# send the url to the owner once, then:
|
|
160
|
+
"$TC_BIN" --profile agent secrets wait OPENAI_API_KEY --timeout 9m --json
|
|
161
|
+
"$TC_BIN" --profile agent secrets get OPENAI_API_KEY --raw
|
|
162
|
+
"$TC_BIN" secrets manage OPENAI_API_KEY --no-open --json # {"url":"…","opened":false}: the replace/manage page
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
- **`secrets request <NAME...> [--scope S | --app ID] [--owner DID] [--requester LABEL] [--open]`** prints `{url, names, scope?, owner}` and exits 0. It is offline: no session, no node request. 1–10 names (`^[A-Z][A-Z0-9_]*$`, ≤ 64, duplicates dropped). `--app ID` derives the scope from an app id (`xyz.tinycloud.feed` → `xyz-tinycloud-feed`); `--app` with `--scope` exits 2. `--requester` defaults to `user@host`. `--open` also opens the link; a failure to open only adds a `BROWSER_OPEN_FAILED` warning on stderr. Errors: 2 for `INVALID_SECRET_NAME`, `TOO_MANY_SECRET_NAMES`, `INVALID_SECRET_SCOPE`, `INVALID_REQUESTER`, `INVALID_OWNER`, `OWNER_MISMATCH`, `LOCAL_OWNER_PROFILE`; 3 for `OWNER_DID_UNKNOWN` (pass `--owner`).
|
|
166
|
+
- **`secrets wait <NAME...> [--scope S | --app ID] [--owner DID] [--timeout D]`** polls until every name is readable. `D` is `0`, `Ns` or `Nm`, default `5m`, at most `9m`; `--timeout 0` makes exactly one check (≤ 21 s). The whole wait is bounded by timeout + 21 s. Each check runs in a child process that is killed if it overruns.
|
|
167
|
+
- **`secrets manage [NAME...] [--scope S | --app ID] [--no-open]`** prints `{url, opened}` for the Secret Manager page, opening a browser unless `--no-open`.
|
|
168
|
+
|
|
169
|
+
**`wait` never signs in: it needs a stored session and doesn't accept `TC_PRIVATE_KEY`** (an explicit key exits 3). It never writes profile state, never saves a consent request, never prints a value and never prompts. After a CLI upgrade, run one normal command per profile first (for example `tc --profile agent auth caps`); otherwise the first `wait` returns `PROFILE_MIGRATION_REQUIRED` once, with that command in `error.recovery.command`.
|
|
170
|
+
|
|
171
|
+
| `wait` exit | Meaning |
|
|
172
|
+
|---|---|
|
|
173
|
+
| 0 | stdout `{status:"ready", names, scope?}` |
|
|
174
|
+
| 8 | `PENDING`: stdout `{status:"pending", pending, present, url}` (or `hint` instead of `url` for a local identity). Rerun `wait` without re-sending the link |
|
|
175
|
+
| 5 | `PERMISSION_DENIED`: `error.recovery` `{action:"request_access", names, owner, scope?}` names the denied names; `error.setup` covers names still pending |
|
|
176
|
+
| 3 | `AUTH_REQUIRED`: no stored session, an expired one, a 401, or `TC_PRIVATE_KEY` set. `error.recovery.command` is the persistent login to run once |
|
|
177
|
+
| 2 | usage, `OWNER_MISMATCH` |
|
|
178
|
+
| 6 | `NETWORK_ERROR` after repeated node failures; `error.progress` `{present, pending}` from the last successful check |
|
|
179
|
+
| 1 | `PROFILE_MIGRATION_REQUIRED` (run `error.recovery.command`, `tc --profile P auth caps`, once, then retry) or `PROBE_FAILED` |
|
|
180
|
+
| 130 | `CANCELLED` (SIGINT/SIGTERM); `error.progress` when a check had succeeded |
|
|
181
|
+
|
|
182
|
+
**Which identity gets a link (posture table):**
|
|
183
|
+
|
|
184
|
+
| Identity | `request` | missing secret on `get` / `wait` |
|
|
185
|
+
|---|---|---|
|
|
186
|
+
| `owner-openkey` (recorded owner) | link for the recorded owner | `error.setup` / `url` |
|
|
187
|
+
| `delegate-session` | link for the owner of its space (or `--owner`) | `error.setup` / `url` |
|
|
188
|
+
| key-only `did:key` with no recorded owner | `OWNER_DID_UNKNOWN` (3) unless `--owner` | `wait`: needs a session (3) |
|
|
189
|
+
| `local-owner-key`, a profile holding a `privateKey`, `--private-key`, `TC_PRIVATE_KEY` | `LOCAL_OWNER_PROFILE` (2) | hint `tc secrets put NAME --stdin`, no link |
|
|
190
|
+
|
|
191
|
+
**Error JSON fields** (stderr, JSON mode; only these keys ever appear in `error`: `code`, `message`, `hint`, `recovery`, `setup`, `progress`, `meta`, `warnings`):
|
|
192
|
+
- `error.setup` `{url, names, scope?, owner, action:"add_secret"}`: the canonical request link, on `NOT_FOUND` and on `wait`'s `PERMISSION_DENIED` for pending names.
|
|
193
|
+
- `error.recovery`: `{action:"sign_in", command?}` (a persistent login), `{action:"complete_migration", command?, operationId:"tinycloud.auth.capabilities"}`, or `{action:"request_access", names, owner, scope?}`. `command` is omitted for profile names that are not shell-safe; sign in to that profile, or run `auth caps` for it, yourself.
|
|
194
|
+
- `error.progress` `{present, pending}`: on `NETWORK_ERROR` and `CANCELLED` from `wait`, only after at least one successful check.
|
|
195
|
+
|
|
148
196
|
### Scoped secret reads for agents
|
|
149
197
|
|
|
150
198
|
An agent profile gets read access to named secrets through a scoped OpenKey paste login with a manifest that names them; [AUTH.md](AUTH.md#secret-reads-for-an-agent) has the full flow. Device login cannot grant secrets.
|
|
@@ -304,9 +352,9 @@ On either recovery path, an orphaned `.recover-*` claim can hold a replacement o
|
|
|
304
352
|
- **Session key**: `did:key:z6Mk...#z6Mk...` — generated at init
|
|
305
353
|
- **Owner DID**: `did:pkh:eip155:{chainId}:{address}` — after auth
|
|
306
354
|
|
|
307
|
-
Use `--json` for machine-readable output; interactive commands can render human output. General command errors go to stderr as `{error: {code, message, hint?, meta?}}
|
|
355
|
+
Use `--json` for machine-readable output; interactive commands can render human output. General command errors go to stderr as `{error: {code, message, hint?, recovery?, setup?, progress?, meta?, warnings?}}` (`recovery`, `setup` and `progress` are described under [Secrets](#asking-the-owner-for-a-secret-request-wait-manage)). `meta` contains a typed HTTP `status` and, only when validated as a TinyCloud capability (and against the KV request when available), a `resource` and `requiredAction`. Inspect the structured code, not only the exit status.
|
|
308
356
|
|
|
309
|
-
General storage/auth exit codes: 0 success, 1 operation error, 2 invalid input, 3 authentication required, 4 not found, 5 permission denied, 6 network error, 7 node error, 10 storage full. `tc share` uses its own: 1 profile lock timeout or sender-history retry (`PROFILE_LOCK_TIMEOUT`, `SHARE_HISTORY_RETRY`), 3 upload authority required, 4 unavailable, expired or upload failed, 5 permission denied (`PERMISSION_DENIED`) or verification failed, 6 recipient authorization required or network error, 7 byte limit, 8 output conflict or unsafe filename, 9 partial success, 10 storage full.
|
|
357
|
+
General storage/auth exit codes: 0 success, 1 operation error (including `PROFILE_MIGRATION_REQUIRED` and `PROBE_FAILED`), 2 invalid input, 3 authentication required, 4 not found, 5 permission denied, 6 network error, 7 node error, 8 pending (`tc secrets wait` only), 10 storage full, 130 interrupted (`CANCELLED`). `tc share` uses its own: 1 profile lock timeout or sender-history retry (`PROFILE_LOCK_TIMEOUT`, `SHARE_HISTORY_RETRY`), 3 upload authority required, 4 unavailable, expired or upload failed, 5 permission denied (`PERMISSION_DENIED`) or verification failed, 6 recipient authorization required or network error, 7 byte limit, 8 output conflict or unsafe filename, 9 partial success, 10 storage full.
|
|
310
358
|
|
|
311
359
|
For `tc kv get|put|head|list|delete`, `tc space list|host`, `tc sql`, `tc vault`, `tc vars`, `tc duckdb`, `tc secrets list|put|delete`, `tc delegation`, and `tc account`, service-result errors use typed authorization: a plain HTTP 401 is `AUTH_REQUIRED` (exit 3); a 403 is `PERMISSION_DENIED` (exit 5). A 401 with a validated missing capability, or `AUTH_UNAUTHORIZED` without an HTTP status, is `PERMISSION_DENIED` (exit 5); a validated capability also produces a `tc auth request --cap` hint. This changes the former exit 1 for status-less `AUTH_UNAUTHORIZED` in the listed commands. Other HTTP failures retain their service-specific codes. `tc share` has separate exit mappings below.
|
|
312
360
|
|