@tiangong-lca/cli 0.1.3 → 0.1.5
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 +43 -11
- package/dist/src/cli.d.ts +4 -0
- package/dist/src/cli.js +264 -7
- package/dist/src/cli.js.map +1 -1
- package/dist/src/lib/auth-identity-receipt.d.ts +2 -2
- package/dist/src/lib/auth-identity-receipt.js +20 -12
- package/dist/src/lib/auth-identity-receipt.js.map +1 -1
- package/dist/src/lib/env.d.ts +8 -0
- package/dist/src/lib/env.js +36 -3
- package/dist/src/lib/env.js.map +1 -1
- package/dist/src/lib/lca-release.d.ts +1 -1
- package/dist/src/lib/lca-release.js +3 -4
- package/dist/src/lib/lca-release.js.map +1 -1
- package/dist/src/lib/lifecyclemodel-resulting-process.js +6 -2
- package/dist/src/lib/lifecyclemodel-resulting-process.js.map +1 -1
- package/dist/src/lib/oauth-loopback.d.ts +52 -0
- package/dist/src/lib/oauth-loopback.js +229 -0
- package/dist/src/lib/oauth-loopback.js.map +1 -0
- package/dist/src/lib/oauth-pkce.d.ts +63 -0
- package/dist/src/lib/oauth-pkce.js +268 -0
- package/dist/src/lib/oauth-pkce.js.map +1 -0
- package/dist/src/lib/process-refresh-references.js +2 -2
- package/dist/src/lib/process-refresh-references.js.map +1 -1
- package/dist/src/lib/process-scope-statistics.js +8 -3
- package/dist/src/lib/process-scope-statistics.js.map +1 -1
- package/dist/src/lib/state-lock.js +10 -4
- package/dist/src/lib/state-lock.js.map +1 -1
- package/dist/src/lib/supabase-client.d.ts +13 -1
- package/dist/src/lib/supabase-client.js +64 -3
- package/dist/src/lib/supabase-client.js.map +1 -1
- package/dist/src/lib/supabase-json-ordered-write.js +7 -3
- package/dist/src/lib/supabase-json-ordered-write.js.map +1 -1
- package/dist/src/lib/supabase-session.d.ts +63 -8
- package/dist/src/lib/supabase-session.js +315 -21
- package/dist/src/lib/supabase-session.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ whenToUpdate:
|
|
|
13
13
|
- when user-facing command examples change
|
|
14
14
|
checkPaths:
|
|
15
15
|
- README.md
|
|
16
|
+
- .env.example
|
|
16
17
|
- package.json
|
|
17
18
|
- pnpm-workspace.yaml
|
|
18
19
|
- pnpm-lock.yaml
|
|
@@ -23,19 +24,22 @@ checkPaths:
|
|
|
23
24
|
- src/batch.ts
|
|
24
25
|
- src/command-spec.ts
|
|
25
26
|
- src/lib/auth-identity-receipt.ts
|
|
27
|
+
- src/lib/oauth-loopback.ts
|
|
28
|
+
- src/lib/oauth-pkce.ts
|
|
29
|
+
- src/lib/supabase-session.ts
|
|
26
30
|
- src/lib/lca-release.ts
|
|
27
31
|
- scripts/run-auth-identity-production-case.ts
|
|
28
32
|
- test/auth-identity*.test.ts
|
|
29
33
|
- test/public-auth-identity-receipt.test.ts
|
|
30
34
|
- test/lca-release*.test.ts
|
|
31
|
-
lastReviewedAt: 2026-08-
|
|
32
|
-
lastReviewedCommit:
|
|
33
|
-
lastReviewedNote: 'Reviewed for Issue #
|
|
35
|
+
lastReviewedAt: 2026-08-31
|
|
36
|
+
lastReviewedCommit: 352c11c5ffafaea1bf94133be92f3337b847866a
|
|
37
|
+
lastReviewedNote: 'Reviewed for Issue #252: identifies 0.1.5 as the replacement OAuth release after unpublished 0.1.4, preserving exports, dependencies, and release behavior.'
|
|
34
38
|
---
|
|
35
39
|
|
|
36
40
|
# TianGong LCA CLI
|
|
37
41
|
|
|
38
|
-
Package: `@tiangong-lca/cli` Executable: `tiangong-lca` Current package version: `0.1.
|
|
42
|
+
Package: `@tiangong-lca/cli` Executable: `tiangong-lca` Current package version: `0.1.5` Node: `24.19.0`
|
|
39
43
|
|
|
40
44
|
Repository development is single-track on pnpm `11.24.0` and TypeScript `7.0.2`. The published package remains a clean, package-manager-neutral consumer artifact: it contains runtime files only, not pnpm, TypeScript, Oxlint, tests, source-only tooling, or repository lockfiles.
|
|
41
45
|
|
|
@@ -59,6 +63,14 @@ Review note, 2026-08-29: Issue #240 adds the typed `@tiangong-lca/cli/auth-ident
|
|
|
59
63
|
|
|
60
64
|
Review note, 2026-08-29: Issue #242 releases that public parser as `@tiangong-lca/cli@0.1.3`. Only package identity and four existing exact-version fixtures change; `./batch`, `./command-spec`, `./auth-identity-receipt`, the executable, dependencies, pnpm lock, Node 24.19.0 / TypeScript 7.0.2 toolchain, and package-manager-neutral consumer contract are otherwise unchanged.
|
|
61
65
|
|
|
66
|
+
Review note, 2026-08-31: Issue #244 adds Supabase OAuth 2.1 Authorization Code + S256 PKCE without adding a dependency or changing package version/exports. `auth login` uses a registered public client, exact literal-`127.0.0.1` callback, state validation, and shell-free system browser; it atomically stores rotating access/refresh tokens in a private session file. `auth status` performs no network request and reveals no credential or session path; `auth whoami` and `auth doctor-auth` use the live redacted identity receipt. `auth logout` deletes only that matching local session; Connected applications owns grant revocation. `TIANGONG_LCA_ACCESS_TOKEN` is a short-lived, online-verified, memory-only headless option. The reversible API key remains only as a transition fallback and is never used by OAuth mode.
|
|
67
|
+
|
|
68
|
+
Review note, 2026-08-31: Issue #247 prevents a second CLI process from failing when the first process removes `session.json.lock` between contention and metadata read. Only an absent lock is treated as released; permission, I/O, malformed-owner, timeout, and stale-owner safety behavior is unchanged.
|
|
69
|
+
|
|
70
|
+
Review note, 2026-08-31: Issue #246 releases that OAuth runtime as `@tiangong-lca/cli@0.1.4`. Only package identity and four existing exact-version fixtures change; the executable and public subpaths, dependencies, pnpm lock, Node 24.19.0 / TypeScript 7.0.2 toolchain, auth behavior, and package-manager-neutral consumer contract are otherwise unchanged.
|
|
71
|
+
|
|
72
|
+
Review note, 2026-08-31: Issue #252 replaces the stopped-unpublished 0.1.4 attempt with `@tiangong-lca/cli@0.1.5` after platform-complete session coverage merged. Only package identity, four version fixtures, and release evidence change; executable/public subpaths, OAuth behavior, dependencies, pnpm lock, Node/TypeScript toolchain, and consumer contract stay unchanged.
|
|
73
|
+
|
|
62
74
|
Review note, 2026-07-12: `dataset maintenance plan/apply/verify` provides current-user RLS-scoped exact-row maintenance with immutable plans, explicit approval, per-action logs, platform audit correlation, and independent readback. `merge-support-aliases` now runs only in `target_mode=owner_draft`: source/target support and all changed rows stay private `state_code=0`; publication is a separate future workflow.
|
|
63
75
|
|
|
64
76
|
Review note, 2026-07-13: maintenance scans now prove exact-count pagination even when PostgREST returns fewer rows than the requested `--page-size`. An incomplete or inconsistent scan fails before artifacts, approval, or mutation; under stable filtered membership/order the proof represents a complete ordered multi-request traversal, not one transaction-level/MVCC snapshot.
|
|
@@ -127,25 +139,45 @@ Remote commands require:
|
|
|
127
139
|
|
|
128
140
|
```bash
|
|
129
141
|
TIANGONG_LCA_API_BASE_URL=
|
|
130
|
-
TIANGONG_LCA_API_KEY=
|
|
131
142
|
TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY=
|
|
143
|
+
TIANGONG_LCA_OAUTH_CLIENT_ID=
|
|
132
144
|
TIANGONG_LCA_REGION=us-east-1
|
|
133
145
|
```
|
|
134
146
|
|
|
135
147
|
Notes:
|
|
136
148
|
|
|
137
149
|
- `TIANGONG_LCA_API_BASE_URL` accepts the project root, `/functions/v1`, or `/rest/v1`.
|
|
138
|
-
- `
|
|
139
|
-
-
|
|
150
|
+
- `TIANGONG_LCA_OAUTH_CLIENT_ID` is the environment-specific registered public CLI client; it is not a secret.
|
|
151
|
+
- Run `tiangong-lca auth login` once in a trusted terminal. All Edge Function and direct Supabase commands then reuse the OAuth access token and rotate the refresh token on demand.
|
|
152
|
+
- For approved headless execution, set `TIANGONG_LCA_AUTH_MODE=access-token` and inject one short-lived `TIANGONG_LCA_ACCESS_TOKEN`. It is verified online, kept only in process memory, and never refreshed.
|
|
153
|
+
- Transition-only compatibility uses `TIANGONG_LCA_AUTH_MODE=legacy-user-api-key` plus `TIANGONG_LCA_API_KEY`. OAuth failures never fall back to this password-equivalent credential.
|
|
140
154
|
|
|
141
155
|
Optional session control:
|
|
142
156
|
|
|
143
157
|
```bash
|
|
158
|
+
TIANGONG_LCA_AUTH_MODE=oauth
|
|
159
|
+
TIANGONG_LCA_OAUTH_REDIRECT_URI=http://127.0.0.1:49191/oauth/callback
|
|
144
160
|
TIANGONG_LCA_SESSION_FILE=
|
|
145
161
|
TIANGONG_LCA_DISABLE_SESSION_CACHE=false
|
|
146
162
|
TIANGONG_LCA_FORCE_REAUTH=false
|
|
147
163
|
```
|
|
148
164
|
|
|
165
|
+
## OAuth Session Commands
|
|
166
|
+
|
|
167
|
+
```text
|
|
168
|
+
tiangong-lca auth login
|
|
169
|
+
tiangong-lca auth status --json
|
|
170
|
+
tiangong-lca auth whoami --json
|
|
171
|
+
tiangong-lca auth doctor-auth --json
|
|
172
|
+
tiangong-lca auth logout
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
The callback URI must exactly match the URI registered with the selected Supabase OAuth client. The default is `http://127.0.0.1:49191/oauth/callback`; OAuth client redirect URIs do not support wildcards. Login never accepts a username, password, authorization code, access token, refresh token, or PKCE verifier through argv. On POSIX, the app directory is `0700` and `session.json` is `0600`; writes and refresh-token rotation use a temporary file, atomic rename, and the existing cross-process state lock. Windows callers must keep the selected parent ACL current-user-only because chmod bits are not an ACL.
|
|
176
|
+
|
|
177
|
+
`auth status` is intentionally local-only and non-mutating. It reports whether a matching session can be used or refreshed, but sets `onlineVerified: false`; it never prints email, tokens, a session path, or a credential fingerprint. `auth whoami` performs the live redacted identity receipt. `auth doctor-auth` first checks local readiness, then performs that live check; a missing OAuth session returns `login-required` so a human can run `auth login`. An AI agent must never ask for or handle the user's password, authorization code, access token, or refresh token.
|
|
178
|
+
|
|
179
|
+
Local logout does not revoke the server grant. To invalidate every refresh token for the CLI client, open Account → Connected applications and disconnect TianGong CLI.
|
|
180
|
+
|
|
149
181
|
## Auth Identity Receipt
|
|
150
182
|
|
|
151
183
|
Use the identity receipt before a production-backed case or any later owner-draft write gate:
|
|
@@ -154,7 +186,7 @@ Use the identity receipt before a production-backed case or any later owner-draf
|
|
|
154
186
|
tiangong-lca auth identity-receipt --expected-project-ref <project-ref> --expected-user-id <user-id> --json
|
|
155
187
|
```
|
|
156
188
|
|
|
157
|
-
The command performs no dataset write. It
|
|
189
|
+
The command performs no dataset write. It resolves the selected OAuth, explicit headless, or transition-only legacy session, checks the canonical Supabase project, and makes a bounded live `GET /auth/v1/user`. A refreshable cached token that receives `401` or `403` may be refreshed and retried exactly once; an explicit headless access token is never refreshed. All other transport or response failures are terminal. A valid production guard requires:
|
|
158
190
|
|
|
159
191
|
- `schema` exactly `tiangong-lca.auth-identity-receipt.v1`;
|
|
160
192
|
- `status: "passed"`, `operation: "current-user-read"`, and `remote_write_mode: "read-only"`;
|
|
@@ -169,7 +201,7 @@ For the explicitly authorized local production read case from a validated reposi
|
|
|
169
201
|
pnpm case:auth-identity:production -- --env-file <data-foundry-ignored-.env> --expected-project-ref <project-ref> --expected-user-id <user-id> --out-dir <new-private-case-directory>
|
|
170
202
|
```
|
|
171
203
|
|
|
172
|
-
The runner reads only `TIANGONG_LCA_API_BASE_URL`, `TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY`, and `TIANGONG_LCA_TEST_API_KEY`; the last is mapped to the child process's
|
|
204
|
+
The historical production-case runner remains a transition-only legacy fixture: it reads only `TIANGONG_LCA_API_BASE_URL`, `TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY`, and `TIANGONG_LCA_TEST_API_KEY`; the last is mapped to the child process's explicit legacy variable. It does not accept an alternate CLI path. The pnpm command first performs a clean TS7 build without the production env. Its plain-Node runner then single-reads source/config/lock and the freshly generated `dist/src/**/*.js`, hashes the source tree, runner, runtime, exact entrypoint, and pnpm lock, and copies those exact built buffers into a private snapshot before exposing the key. It forces reauthentication with session cache disabled, runs only the built snapshot from an exclusively created clean directory with an argv array and `shell:false`, cleans the snapshot before publishing success artifacts, and persists only the parsed receipt and case manifest. POSIX creates the case directory as `0700` and files as `0600`; Windows inherits ACLs from the caller-selected parent, so use a user-restricted parent because mode bits are not an ACL guarantee. The runner never stores raw child stdout/stderr and is intentionally not wired to CI secrets. New automation should use OAuth/headless mode instead. This receipt is locally hash-verifiable, not server-signed attestation.
|
|
173
205
|
|
|
174
206
|
## LCI/LCIA Data Release
|
|
175
207
|
|
|
@@ -235,7 +267,7 @@ tiangong-lca release status --release-run-id <release-run-id> --json
|
|
|
235
267
|
}
|
|
236
268
|
```
|
|
237
269
|
|
|
238
|
-
The CLI verifies every local upload against its declared byte size, SHA-256, media type, and required pair before requesting signed URLs. Upload receipts and downloads are written atomically with private file permissions. Existing outputs are preserved unless `--force` is explicit.
|
|
270
|
+
The CLI verifies every local upload against its declared byte size, SHA-256, media type, and required pair before requesting signed URLs. Upload receipts and downloads are written atomically with private file permissions. Existing outputs are preserved unless `--force` is explicit. Release requests use the resolved actor access token and `client_id`-aware database policy; no credential-derived fingerprint is accepted from local state or the request file.
|
|
239
271
|
|
|
240
272
|
Calculation results and published artifacts remain file-first:
|
|
241
273
|
|
|
@@ -325,7 +357,7 @@ Key outputs under `--out-dir`:
|
|
|
325
357
|
- `outputs/identity-candidates.jsonl`
|
|
326
358
|
- `outputs/identity-candidate-sources.json`
|
|
327
359
|
|
|
328
|
-
`--candidate-input` is repeatable and accepts JSON, JSONL, or a directory scanned recursively for JSON/JSONL candidate rows. Embedded `candidates` from the request and local-scan candidates are evaluated together. Add `--remote-candidates` when the preflight should also call `process_hybrid_search` or `flow_hybrid_search`; `--remote-query` overrides the target-derived search text and `--remote-limit` caps returned candidate rows. Remote candidate search uses the normal
|
|
360
|
+
`--candidate-input` is repeatable and accepts JSON, JSONL, or a directory scanned recursively for JSON/JSONL candidate rows. Embedded `candidates` from the request and local-scan candidates are evaluated together. Add `--remote-candidates` when the preflight should also call `process_hybrid_search` or `flow_hybrid_search`; `--remote-query` overrides the target-derived search text and `--remote-limit` caps returned candidate rows. Remote candidate search uses the normal OAuth session env above and optional `TIANGONG_LCA_REGION`.
|
|
329
361
|
|
|
330
362
|
When remote search is enabled, the CLI sends a compact fielded `query` string plus supported edge-search options to `process_hybrid_search` or `flow_hybrid_search`: `filter`, `match_count`, `page_size`, `data_source`, `match_threshold`, `lexical_weight`, `semantic_weight`, and `rrf_k`. `lexical_weight` controls the single database-owned `extracted_md` lexical branch. Request-level `remote_candidate_search.profile_hints` are not sent to the Edge Function. They are applied locally before scoring candidates so Foundry can provide source-derived facts such as flow type, flow property, reference unit, elementary categories, geography, reference-flow names, technology route, and system boundary without polluting the full-text/semantic query.
|
|
331
363
|
|
package/dist/src/cli.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DotEnvLoadResult } from './lib/dotenv.js';
|
|
2
2
|
import type { FetchLike } from './lib/http.js';
|
|
3
|
+
import { inspectSupabaseAuthStatus, loginWithSupabaseOAuth, logoutSupabaseUserSession } from './lib/supabase-session.js';
|
|
3
4
|
import { type AuthIdentityReceipt, type RunAuthIdentityReceiptOptions } from './lib/auth-identity-receipt.js';
|
|
4
5
|
import { type LifecyclemodelAutoBuildReport, type RunLifecyclemodelAutoBuildOptions } from './lib/lifecyclemodel-auto-build.js';
|
|
5
6
|
import { type LifecyclemodelResultingProcessReport, type RunLifecyclemodelResultingProcessOptions } from './lib/lifecyclemodel-resulting-process.js';
|
|
@@ -71,6 +72,9 @@ export type CliDeps = {
|
|
|
71
72
|
dotEnvStatus: DotEnvLoadResult;
|
|
72
73
|
fetchImpl: FetchLike;
|
|
73
74
|
runAuthIdentityReceiptImpl?: (options: RunAuthIdentityReceiptOptions) => Promise<AuthIdentityReceipt>;
|
|
75
|
+
loginWithSupabaseOAuthImpl?: typeof loginWithSupabaseOAuth;
|
|
76
|
+
logoutSupabaseUserSessionImpl?: typeof logoutSupabaseUserSession;
|
|
77
|
+
inspectSupabaseAuthStatusImpl?: typeof inspectSupabaseAuthStatus;
|
|
74
78
|
runPublishImpl?: (options: RunPublishOptions) => Promise<PublishReport>;
|
|
75
79
|
runValidationImpl?: (options: RunValidationOptions) => Promise<ValidationRunReport>;
|
|
76
80
|
runLcaReleaseImpl?: (options: RunLcaReleaseOptions) => Promise<LcaReleaseReport>;
|
package/dist/src/cli.js
CHANGED
|
@@ -3,6 +3,9 @@ import { buildDoctorReport, readRuntimeEnv } from './lib/env.js';
|
|
|
3
3
|
import { CliError, toErrorPayload } from './lib/errors.js';
|
|
4
4
|
import { stringifyJson } from './lib/io.js';
|
|
5
5
|
import { loadCliPackageVersion } from './lib/package-version.js';
|
|
6
|
+
import { requireSupabaseRestRuntime } from './lib/supabase-client.js';
|
|
7
|
+
import { inspectSupabaseAuthStatus, loginWithSupabaseOAuth, logoutSupabaseUserSession, } from './lib/supabase-session.js';
|
|
8
|
+
import { OAUTH_LOGIN_TIMEOUT_MAX_MS } from './lib/oauth-loopback.js';
|
|
6
9
|
import { AUTH_IDENTITY_MAX_TIMEOUT_MS, AUTH_IDENTITY_RECEIPT_SCHEMA, runAuthIdentityReceipt, } from './lib/auth-identity-receipt.js';
|
|
7
10
|
import { runLifecyclemodelAutoBuild, } from './lib/lifecyclemodel-auto-build.js';
|
|
8
11
|
import { runLifecyclemodelBuildResultingProcess, } from './lib/lifecyclemodel-resulting-process.js';
|
|
@@ -87,7 +90,7 @@ Usage:
|
|
|
87
90
|
Commands:
|
|
88
91
|
Implemented Commands:
|
|
89
92
|
doctor show environment diagnostics
|
|
90
|
-
auth identity-receipt
|
|
93
|
+
auth login | status | whoami | doctor-auth | logout | identity-receipt
|
|
91
94
|
search flow | process | lifecyclemodel
|
|
92
95
|
process get | list | identity-preflight | build-plan | scope-statistics | dedup-review | auto-build | resume-build | publish-build | complete-required-fields | save-draft | batch-build | refresh-references | verify-rows
|
|
93
96
|
dataset contract get | context-pack | classification children/path/audit/apply | curation-queue build/next/verify | import-lca convert | author | patch apply | save-draft | source upload-attachments | validate | verify-remote | bilingual extract/apply/validate | evidence-search plan/run | references rewrite/refresh-remote | maintenance clear-account/plan/apply/verify/flow-identity
|
|
@@ -100,13 +103,17 @@ Implemented Commands:
|
|
|
100
103
|
admin embedding-run
|
|
101
104
|
|
|
102
105
|
Planned Surface (not implemented yet):
|
|
103
|
-
auth whoami | doctor-auth
|
|
104
106
|
job get | wait | logs
|
|
105
107
|
|
|
106
108
|
Planned commands currently print an explicit "not implemented yet" message and exit with code 2.
|
|
107
109
|
|
|
108
110
|
Examples:
|
|
109
111
|
tiangong-lca doctor
|
|
112
|
+
tiangong-lca auth login
|
|
113
|
+
tiangong-lca auth status --json
|
|
114
|
+
tiangong-lca auth whoami --json
|
|
115
|
+
tiangong-lca auth doctor-auth --json
|
|
116
|
+
tiangong-lca auth logout
|
|
110
117
|
tiangong-lca auth identity-receipt --expected-project-ref <project-ref> --expected-user-id <user-id> --json
|
|
111
118
|
tiangong-lca search flow --input ./request.json
|
|
112
119
|
tiangong-lca search process --input ./request.json --dry-run
|
|
@@ -185,15 +192,79 @@ Environment:
|
|
|
185
192
|
}
|
|
186
193
|
function renderAuthHelp() {
|
|
187
194
|
return `Usage:
|
|
188
|
-
tiangong-lca auth identity-receipt [options]
|
|
195
|
+
tiangong-lca auth <login|status|whoami|doctor-auth|logout|identity-receipt> [options]
|
|
189
196
|
|
|
190
197
|
Implemented Subcommands:
|
|
198
|
+
login Authorize the registered public CLI through browser PKCE and store a private rotating session
|
|
199
|
+
status Inspect only local auth readiness without reading or printing credentials
|
|
200
|
+
whoami Run the live, redacted identity receipt for the current user
|
|
201
|
+
doctor-auth Combine local readiness with a live, redacted identity check
|
|
202
|
+
logout Remove only this local CLI session; revoke the connected-app grant from the account page
|
|
191
203
|
identity-receipt Live, read-only proof of the authenticated account and Supabase project
|
|
192
204
|
|
|
193
|
-
|
|
194
|
-
|
|
205
|
+
Use "tiangong-lca auth <subcommand> --help" for details.
|
|
206
|
+
`.trim();
|
|
207
|
+
}
|
|
208
|
+
function renderAuthStatusHelp() {
|
|
209
|
+
return `Usage:
|
|
210
|
+
tiangong-lca auth status [--json]
|
|
211
|
+
|
|
212
|
+
Inspects only the matching local session metadata. It performs no network request, never refreshes
|
|
213
|
+
a token, and never emits email, token, session path, or credential fingerprint. A ready result can
|
|
214
|
+
still be remotely revoked; use "auth doctor-auth" when live verification is required. If OAuth login
|
|
215
|
+
is required, hand the terminal to the human user and run "tiangong-lca auth login". Never ask an AI
|
|
216
|
+
agent to collect a username, password, authorization code, access token, or refresh token.
|
|
217
|
+
`.trim();
|
|
218
|
+
}
|
|
219
|
+
function renderAuthWhoamiHelp() {
|
|
220
|
+
return `Usage:
|
|
221
|
+
tiangong-lca auth whoami [--timeout-ms <n>] [--json]
|
|
222
|
+
|
|
223
|
+
Runs the same live, read-only and redacted proof as "auth identity-receipt" without requiring an
|
|
224
|
+
expected identity assertion. It may refresh an OAuth session under the private session lock. Output
|
|
225
|
+
contains no token, full email address, session path, or credential fingerprint.
|
|
226
|
+
`.trim();
|
|
227
|
+
}
|
|
228
|
+
function renderAuthDoctorHelp() {
|
|
229
|
+
return `Usage:
|
|
230
|
+
tiangong-lca auth doctor-auth [--timeout-ms <n>] [--json]
|
|
231
|
+
|
|
232
|
+
First checks local readiness without network access. If ready, it performs the live redacted identity
|
|
233
|
+
receipt. Missing OAuth authorization returns login-required without attempting password bootstrap.
|
|
234
|
+
Run "tiangong-lca auth login" only in a trusted human-controlled terminal.
|
|
235
|
+
`.trim();
|
|
236
|
+
}
|
|
237
|
+
function renderAuthLoginHelp() {
|
|
238
|
+
return `Usage:
|
|
239
|
+
tiangong-lca auth login [options]
|
|
240
|
+
|
|
241
|
+
Options:
|
|
242
|
+
--timeout-ms <n> Browser callback timeout 1..${OAUTH_LOGIN_TIMEOUT_MAX_MS} ms (default: 180000)
|
|
243
|
+
--json Print compact JSON; default output is pretty JSON
|
|
244
|
+
-h, --help
|
|
245
|
+
|
|
246
|
+
Required env:
|
|
247
|
+
TIANGONG_LCA_API_BASE_URL
|
|
248
|
+
TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY
|
|
249
|
+
TIANGONG_LCA_OAUTH_CLIENT_ID
|
|
250
|
+
|
|
251
|
+
Optional env:
|
|
252
|
+
TIANGONG_LCA_AUTH_MODE=oauth
|
|
253
|
+
TIANGONG_LCA_OAUTH_REDIRECT_URI (default: exact registered 127.0.0.1 callback)
|
|
254
|
+
TIANGONG_LCA_SESSION_FILE
|
|
255
|
+
|
|
256
|
+
The CLI binds only 127.0.0.1, opens the system browser without a shell, verifies state and S256
|
|
257
|
+
PKCE, and atomically writes access/rotating refresh tokens to a private session file. It never
|
|
258
|
+
accepts a username, password, authorization code, access token, or refresh token as an argv value.
|
|
259
|
+
`.trim();
|
|
260
|
+
}
|
|
261
|
+
function renderAuthLogoutHelp() {
|
|
262
|
+
return `Usage:
|
|
263
|
+
tiangong-lca auth logout [--json]
|
|
195
264
|
|
|
196
|
-
|
|
265
|
+
Removes only the locally cached session bound to the current project/client. To revoke the OAuth
|
|
266
|
+
grant and every refresh token, disconnect TianGong CLI from Connected applications in the account
|
|
267
|
+
page. An explicit TIANGONG_LCA_ACCESS_TOKEN is never persisted and therefore has nothing to delete.
|
|
197
268
|
`.trim();
|
|
198
269
|
}
|
|
199
270
|
function renderAuthIdentityReceiptHelp() {
|
|
@@ -218,8 +289,14 @@ Safety:
|
|
|
218
289
|
|
|
219
290
|
Required env:
|
|
220
291
|
TIANGONG_LCA_API_BASE_URL
|
|
221
|
-
TIANGONG_LCA_API_KEY
|
|
222
292
|
TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY
|
|
293
|
+
TIANGONG_LCA_OAUTH_CLIENT_ID and a prior "tiangong-lca auth login"
|
|
294
|
+
|
|
295
|
+
Headless alternative:
|
|
296
|
+
TIANGONG_LCA_ACCESS_TOKEN (short-lived actor token; never cached or refreshed)
|
|
297
|
+
|
|
298
|
+
Legacy compatibility:
|
|
299
|
+
TIANGONG_LCA_AUTH_MODE=legacy-user-api-key plus TIANGONG_LCA_API_KEY
|
|
223
300
|
|
|
224
301
|
Optional session env:
|
|
225
302
|
TIANGONG_LCA_SESSION_FILE
|
|
@@ -2369,6 +2446,99 @@ function parseAuthIdentityReceiptFlags(args) {
|
|
|
2369
2446
|
timeoutMs,
|
|
2370
2447
|
};
|
|
2371
2448
|
}
|
|
2449
|
+
function parseAuthLoginFlags(args) {
|
|
2450
|
+
let parsed;
|
|
2451
|
+
try {
|
|
2452
|
+
parsed = parseArgs({
|
|
2453
|
+
args,
|
|
2454
|
+
allowPositionals: false,
|
|
2455
|
+
strict: true,
|
|
2456
|
+
tokens: true,
|
|
2457
|
+
options: {
|
|
2458
|
+
help: { type: 'boolean', short: 'h' },
|
|
2459
|
+
json: { type: 'boolean' },
|
|
2460
|
+
'timeout-ms': { type: 'string' },
|
|
2461
|
+
},
|
|
2462
|
+
});
|
|
2463
|
+
}
|
|
2464
|
+
catch (error) {
|
|
2465
|
+
throw new CliError(String(error), { code: 'INVALID_ARGS', exitCode: 2 });
|
|
2466
|
+
}
|
|
2467
|
+
const timeoutOccurrences = parsed.tokens.filter((entry) => entry.kind === 'option' && entry.name === 'timeout-ms').length;
|
|
2468
|
+
if (timeoutOccurrences > 1) {
|
|
2469
|
+
throw new CliError('Option --timeout-ms may be provided only once.', {
|
|
2470
|
+
code: 'INVALID_ARGS',
|
|
2471
|
+
exitCode: 2,
|
|
2472
|
+
});
|
|
2473
|
+
}
|
|
2474
|
+
const timeoutText = parsed.values['timeout-ms'];
|
|
2475
|
+
const timeoutMs = typeof timeoutText === 'string' ? Number(timeoutText) : 180_000;
|
|
2476
|
+
if (!Number.isSafeInteger(timeoutMs) ||
|
|
2477
|
+
timeoutMs <= 0 ||
|
|
2478
|
+
timeoutMs > OAUTH_LOGIN_TIMEOUT_MAX_MS) {
|
|
2479
|
+
throw new CliError(`Expected --timeout-ms to be an integer between 1 and ${OAUTH_LOGIN_TIMEOUT_MAX_MS}.`, { code: 'INVALID_TIMEOUT', exitCode: 2 });
|
|
2480
|
+
}
|
|
2481
|
+
return {
|
|
2482
|
+
help: Boolean(parsed.values.help),
|
|
2483
|
+
json: Boolean(parsed.values.json),
|
|
2484
|
+
timeoutMs,
|
|
2485
|
+
};
|
|
2486
|
+
}
|
|
2487
|
+
function parseAuthLogoutFlags(args) {
|
|
2488
|
+
try {
|
|
2489
|
+
const { values } = parseArgs({
|
|
2490
|
+
args,
|
|
2491
|
+
allowPositionals: false,
|
|
2492
|
+
strict: true,
|
|
2493
|
+
options: {
|
|
2494
|
+
help: { type: 'boolean', short: 'h' },
|
|
2495
|
+
json: { type: 'boolean' },
|
|
2496
|
+
},
|
|
2497
|
+
});
|
|
2498
|
+
return { help: Boolean(values.help), json: Boolean(values.json) };
|
|
2499
|
+
}
|
|
2500
|
+
catch (error) {
|
|
2501
|
+
throw new CliError(String(error), { code: 'INVALID_ARGS', exitCode: 2 });
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
function parseAuthLiveFlags(args) {
|
|
2505
|
+
let parsed;
|
|
2506
|
+
try {
|
|
2507
|
+
parsed = parseArgs({
|
|
2508
|
+
args,
|
|
2509
|
+
allowPositionals: false,
|
|
2510
|
+
strict: true,
|
|
2511
|
+
tokens: true,
|
|
2512
|
+
options: {
|
|
2513
|
+
help: { type: 'boolean', short: 'h' },
|
|
2514
|
+
json: { type: 'boolean' },
|
|
2515
|
+
'timeout-ms': { type: 'string' },
|
|
2516
|
+
},
|
|
2517
|
+
});
|
|
2518
|
+
}
|
|
2519
|
+
catch (error) {
|
|
2520
|
+
throw new CliError(String(error), { code: 'INVALID_ARGS', exitCode: 2 });
|
|
2521
|
+
}
|
|
2522
|
+
const timeoutOccurrences = parsed.tokens.filter((entry) => entry.kind === 'option' && entry.name === 'timeout-ms').length;
|
|
2523
|
+
if (timeoutOccurrences > 1) {
|
|
2524
|
+
throw new CliError('Option --timeout-ms may be provided only once.', {
|
|
2525
|
+
code: 'INVALID_ARGS',
|
|
2526
|
+
exitCode: 2,
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
2529
|
+
const timeoutText = parsed.values['timeout-ms'];
|
|
2530
|
+
const timeoutMs = typeof timeoutText === 'string' ? Number(timeoutText) : 10_000;
|
|
2531
|
+
if (!Number.isSafeInteger(timeoutMs) ||
|
|
2532
|
+
timeoutMs <= 0 ||
|
|
2533
|
+
timeoutMs > AUTH_IDENTITY_MAX_TIMEOUT_MS) {
|
|
2534
|
+
throw new CliError(`Expected --timeout-ms to be an integer between 1 and ${AUTH_IDENTITY_MAX_TIMEOUT_MS}.`, { code: 'INVALID_TIMEOUT', exitCode: 2 });
|
|
2535
|
+
}
|
|
2536
|
+
return {
|
|
2537
|
+
help: Boolean(parsed.values.help),
|
|
2538
|
+
json: Boolean(parsed.values.json),
|
|
2539
|
+
timeoutMs,
|
|
2540
|
+
};
|
|
2541
|
+
}
|
|
2372
2542
|
function parseRemoteFlags(args) {
|
|
2373
2543
|
let values;
|
|
2374
2544
|
try {
|
|
@@ -5543,6 +5713,9 @@ export async function executeCli(argv, deps) {
|
|
|
5543
5713
|
try {
|
|
5544
5714
|
const { flags, command, subcommand, commandArgs } = parseCommandLine(argv);
|
|
5545
5715
|
const authIdentityReceiptImpl = deps.runAuthIdentityReceiptImpl ?? runAuthIdentityReceipt;
|
|
5716
|
+
const oauthLoginImpl = deps.loginWithSupabaseOAuthImpl ?? loginWithSupabaseOAuth;
|
|
5717
|
+
const oauthLogoutImpl = deps.logoutSupabaseUserSessionImpl ?? logoutSupabaseUserSession;
|
|
5718
|
+
const authStatusImpl = deps.inspectSupabaseAuthStatusImpl ?? inspectSupabaseAuthStatus;
|
|
5546
5719
|
const publishImpl = deps.runPublishImpl ?? runPublish;
|
|
5547
5720
|
const validationImpl = deps.runValidationImpl ?? runValidation;
|
|
5548
5721
|
const lcaReleaseImpl = deps.runLcaReleaseImpl ?? runLcaRelease;
|
|
@@ -5671,6 +5844,90 @@ export async function executeCli(argv, deps) {
|
|
|
5671
5844
|
stderr: '',
|
|
5672
5845
|
};
|
|
5673
5846
|
}
|
|
5847
|
+
if (command === 'auth' && subcommand === 'status') {
|
|
5848
|
+
const authFlags = parseAuthLogoutFlags(commandArgs);
|
|
5849
|
+
if (authFlags.help) {
|
|
5850
|
+
return { exitCode: 0, stdout: `${renderAuthStatusHelp()}\n`, stderr: '' };
|
|
5851
|
+
}
|
|
5852
|
+
const receipt = authStatusImpl({ runtime: requireSupabaseRestRuntime(deps.env) });
|
|
5853
|
+
return {
|
|
5854
|
+
exitCode: receipt.status === 'ready' ? 0 : 1,
|
|
5855
|
+
stdout: stringifyJson(receipt, authFlags.json),
|
|
5856
|
+
stderr: '',
|
|
5857
|
+
};
|
|
5858
|
+
}
|
|
5859
|
+
if (command === 'auth' && subcommand === 'whoami') {
|
|
5860
|
+
const authFlags = parseAuthLiveFlags(commandArgs);
|
|
5861
|
+
if (authFlags.help) {
|
|
5862
|
+
return { exitCode: 0, stdout: `${renderAuthWhoamiHelp()}\n`, stderr: '' };
|
|
5863
|
+
}
|
|
5864
|
+
const receipt = await authIdentityReceiptImpl({
|
|
5865
|
+
env: deps.env,
|
|
5866
|
+
fetchImpl: deps.fetchImpl,
|
|
5867
|
+
cliVersion: loadCliPackageVersion(import.meta.url),
|
|
5868
|
+
expectedProjectRef: null,
|
|
5869
|
+
expectedUserId: null,
|
|
5870
|
+
timeoutMs: authFlags.timeoutMs,
|
|
5871
|
+
});
|
|
5872
|
+
return { exitCode: 0, stdout: stringifyJson(receipt, authFlags.json), stderr: '' };
|
|
5873
|
+
}
|
|
5874
|
+
if (command === 'auth' && subcommand === 'doctor-auth') {
|
|
5875
|
+
const authFlags = parseAuthLiveFlags(commandArgs);
|
|
5876
|
+
if (authFlags.help) {
|
|
5877
|
+
return { exitCode: 0, stdout: `${renderAuthDoctorHelp()}\n`, stderr: '' };
|
|
5878
|
+
}
|
|
5879
|
+
const local = authStatusImpl({ runtime: requireSupabaseRestRuntime(deps.env) });
|
|
5880
|
+
if (local.status !== 'ready') {
|
|
5881
|
+
return {
|
|
5882
|
+
exitCode: 1,
|
|
5883
|
+
stdout: stringifyJson({
|
|
5884
|
+
schemaVersion: 'tiangong.cli-auth-doctor.v1',
|
|
5885
|
+
status: 'login-required',
|
|
5886
|
+
local,
|
|
5887
|
+
live: null,
|
|
5888
|
+
}, authFlags.json),
|
|
5889
|
+
stderr: '',
|
|
5890
|
+
};
|
|
5891
|
+
}
|
|
5892
|
+
const live = await authIdentityReceiptImpl({
|
|
5893
|
+
env: deps.env,
|
|
5894
|
+
fetchImpl: deps.fetchImpl,
|
|
5895
|
+
cliVersion: loadCliPackageVersion(import.meta.url),
|
|
5896
|
+
expectedProjectRef: null,
|
|
5897
|
+
expectedUserId: null,
|
|
5898
|
+
timeoutMs: authFlags.timeoutMs,
|
|
5899
|
+
});
|
|
5900
|
+
return {
|
|
5901
|
+
exitCode: 0,
|
|
5902
|
+
stdout: stringifyJson({
|
|
5903
|
+
schemaVersion: 'tiangong.cli-auth-doctor.v1',
|
|
5904
|
+
status: 'passed',
|
|
5905
|
+
local,
|
|
5906
|
+
live,
|
|
5907
|
+
}, authFlags.json),
|
|
5908
|
+
stderr: '',
|
|
5909
|
+
};
|
|
5910
|
+
}
|
|
5911
|
+
if (command === 'auth' && subcommand === 'login') {
|
|
5912
|
+
const authFlags = parseAuthLoginFlags(commandArgs);
|
|
5913
|
+
if (authFlags.help) {
|
|
5914
|
+
return { exitCode: 0, stdout: `${renderAuthLoginHelp()}\n`, stderr: '' };
|
|
5915
|
+
}
|
|
5916
|
+
const receipt = await oauthLoginImpl({
|
|
5917
|
+
runtime: requireSupabaseRestRuntime(deps.env),
|
|
5918
|
+
fetchImpl: deps.fetchImpl,
|
|
5919
|
+
loginTimeoutMs: authFlags.timeoutMs,
|
|
5920
|
+
});
|
|
5921
|
+
return { exitCode: 0, stdout: stringifyJson(receipt, authFlags.json), stderr: '' };
|
|
5922
|
+
}
|
|
5923
|
+
if (command === 'auth' && subcommand === 'logout') {
|
|
5924
|
+
const authFlags = parseAuthLogoutFlags(commandArgs);
|
|
5925
|
+
if (authFlags.help) {
|
|
5926
|
+
return { exitCode: 0, stdout: `${renderAuthLogoutHelp()}\n`, stderr: '' };
|
|
5927
|
+
}
|
|
5928
|
+
const receipt = await oauthLogoutImpl({ runtime: requireSupabaseRestRuntime(deps.env) });
|
|
5929
|
+
return { exitCode: 0, stdout: stringifyJson(receipt, authFlags.json), stderr: '' };
|
|
5930
|
+
}
|
|
5674
5931
|
if (command === 'search' && !subcommand && commandArgs.includes('--help')) {
|
|
5675
5932
|
return { exitCode: 0, stdout: `${renderSearchHelp()}\n`, stderr: '' };
|
|
5676
5933
|
}
|