@grantjs/cli 1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alejandro Heredia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,150 @@
1
+ # @grantjs/cli
2
+
3
+ Grant CLI for setup, authentication, profiles, and typings generation.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add -g @grantjs/cli
9
+ # or
10
+ npm install -g @grantjs/cli
11
+ ```
12
+
13
+ ## Commands overview
14
+
15
+ | Command | Description |
16
+ | ----------------------------- | ---------------------------------------------------------------------------------------------- |
17
+ | `grant version` | Show CLI version (`-j, --json` for JSON) |
18
+ | `grant help` / `grant --help` | Show help |
19
+ | `grant start` | Interactive setup (API URL, auth, profile, scope); alias: `grant setup` |
20
+ | `grant generate-types` | Generate project-scoped `ResourceSlug` and `ResourceAction` TypeScript (uses selected profile) |
21
+ | `grant config path` | Print path to the config file |
22
+ | `grant config list` | List profiles and which is default |
23
+ | `grant config show` | Show config summary for a profile (no secrets) |
24
+ | `grant config set <key>` | Set a config value for a profile (see below) |
25
+
26
+ All commands that use config accept **`-p, --profile <name>`** to target a profile (default: the configured default profile).
27
+
28
+ ---
29
+
30
+ ## Setup: `grant start`
31
+
32
+ Interactive flow:
33
+
34
+ 1. **Grant API base URL** – Default: `http://localhost:4000` (or existing value).
35
+ 2. **Authentication method** – **Session** (browser login) or **API key**.
36
+ 3. **Profile name** – Name for this config (e.g. `default`, `staging`). Override with `--profile <name>` to skip the prompt.
37
+ 4. **Session flow** – Sign in with **Email** (email + password) or **GitHub** (browser OAuth). Then choose account → organization (if applicable) → project.
38
+ 5. **API key flow** – Client ID (UUID), client secret (min 32 chars), scope tenant (`accountProject` / `organizationProject`), scope ID (e.g. `accountId:projectId` or `organizationId:projectId`).
39
+ 6. **Default output for generate-types** – Optional path (e.g. `./src/grant-types.ts`). Leave empty for `./grant-types.ts`.
40
+
41
+ Config is saved to the platform config dir (e.g. `~/.config/grant/config.json` on Linux/macOS). The first profile created becomes the default; change it with `grant config set default-profile <name>`.
42
+
43
+ ### Session authentication
44
+
45
+ - **Email** – You are prompted for email and password; the CLI calls the Grant API login endpoint and stores the returned access and refresh tokens.
46
+ - **GitHub** – The CLI starts a temporary local HTTP server and opens your browser to the Grant API’s GitHub OAuth URL with a `redirect` to `http://localhost:<port>`. After you sign in with GitHub, the API redirects back to that URL with a one-time code. The CLI exchanges the code for access and refresh tokens via `POST /api/auth/cli-callback`. No tokens are sent in the URL; the code is single-use and short-lived (e.g. 60 seconds). The API only accepts `localhost` (or `127.0.0.1`) as the redirect for this flow.
47
+
48
+ **Examples:**
49
+
50
+ ```bash
51
+ grant start
52
+ grant start --profile staging
53
+ ```
54
+
55
+ ---
56
+
57
+ ## Generate types: `grant generate-types`
58
+
59
+ Loads the selected profile, exchanges API key for a token if needed, fetches resources and permissions for the project scope, and writes a TypeScript file with `ResourceSlug` and `ResourceAction` constants.
60
+
61
+ - **`-o, --output <path>`** – Output file (default: profile’s `generateTypesOutputPath` or `./grant-types.ts`).
62
+ - **`--dry-run`** – Print what would be generated without writing.
63
+ - **`-p, --profile <name>`** – Profile to use (default: default profile).
64
+
65
+ **Examples:**
66
+
67
+ ```bash
68
+ grant generate-types
69
+ grant generate-types --profile staging -o ./src/grant-types.ts
70
+ grant generate-types --dry-run
71
+ ```
72
+
73
+ ---
74
+
75
+ ## Config: `grant config`
76
+
77
+ ### `grant config path`
78
+
79
+ Prints the path to the config file (no profile).
80
+
81
+ ### `grant config list`
82
+
83
+ Lists profile names and marks the default.
84
+
85
+ ### `grant config show`
86
+
87
+ Shows config summary for a profile (API URL, auth method, scope, generate-types output; no secrets).
88
+
89
+ - **`-p, --profile <name>`** – Profile to show (default: default profile).
90
+
91
+ ### `grant config set`
92
+
93
+ Set a value for a profile. Use **`-p, --profile <name>`** to target a profile (default: default profile).
94
+
95
+ | Subcommand | Description |
96
+ | --------------------------------------------------- | ------------------------------------------------------------------- |
97
+ | `grant config set api-url <url>` | Set Grant API base URL (e.g. `http://localhost:4000`) |
98
+ | `grant config set auth-method <session \| api-key>` | Set authentication method |
99
+ | `grant config set credentials` | Set API key and scope (see options below) |
100
+ | `grant config set scope` | Set selected project scope only |
101
+ | `grant config set generate-types-output <path>` | Set default output path for `grant generate-types` (empty to clear) |
102
+ | `grant config set default-profile <name>` | Set which profile is used when `--profile` is omitted |
103
+
104
+ **Credentials options** (all required for `credentials`):
105
+
106
+ - `--client-id <id>` – API key client ID (UUID)
107
+ - `--client-secret <secret>` – API key client secret (min 32 characters)
108
+ - `--scope-tenant <tenant>` – `accountProject` or `organizationProject`
109
+ - `--scope-id <id>` – e.g. `accountId:projectId` or `organizationId:projectId`
110
+
111
+ **Scope options** (for `scope`):
112
+
113
+ - `--tenant <tenant>` – `accountProject` or `organizationProject`
114
+ - `--scope-id <id>` – Scope ID string
115
+
116
+ **Examples:**
117
+
118
+ ```bash
119
+ grant config set api-url http://localhost:4000
120
+ grant config set api-url http://localhost:4000 --profile staging
121
+ grant config set credentials --client-id <uuid> --client-secret <secret> --scope-tenant organizationProject --scope-id <orgId>:<projectId>
122
+ grant config set scope --tenant organizationProject --scope-id <orgId>:<projectId>
123
+ grant config set generate-types-output ./src/grant-types.ts
124
+ grant config set default-profile staging
125
+ ```
126
+
127
+ ---
128
+
129
+ ## Development (from monorepo)
130
+
131
+ ```bash
132
+ pnpm --filter @grantjs/cli run build
133
+ node packages/@grantjs/cli/dist/index.mjs version
134
+ # or link globally: from packages/@grantjs/cli run pnpm link --global
135
+ ```
136
+
137
+ **Interactive TUI:** Run `grant start` (and any prompts) in a real terminal so the process has a TTY. Running via IDE "Run" or in CI often has no stdin and prompts will not work.
138
+
139
+ **Documentation:** [Grant CLI](https://github.com/logusgraphics/grant/blob/main/docs/integration/cli.md) in the official docs.
140
+
141
+ **Publishing:** See [RELEASE.md](./RELEASE.md) for versioning and npm publish (Changesets).
142
+
143
+ **Tests:**
144
+
145
+ ```bash
146
+ pnpm --filter @grantjs/cli test
147
+ pnpm --filter @grantjs/cli test:watch
148
+ ```
149
+
150
+ ---
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Minimal REST client for Grant API.
3
+ * Used by start (token exchange) and generate-types (resources/permissions).
4
+ */
5
+ export interface TokenExchangeScope {
6
+ id: string;
7
+ tenant: string;
8
+ }
9
+ export interface TokenExchangeRequest {
10
+ clientId: string;
11
+ clientSecret: string;
12
+ scope: TokenExchangeScope;
13
+ }
14
+ export interface TokenExchangeResponse {
15
+ accessToken: string;
16
+ expiresIn: number;
17
+ }
18
+ export interface ApiErrorBody {
19
+ success?: false;
20
+ error?: {
21
+ code?: string;
22
+ message?: string;
23
+ };
24
+ reason?: string;
25
+ code?: string;
26
+ }
27
+ export interface LoginAccount {
28
+ id: string;
29
+ type: string;
30
+ ownerId: string | null;
31
+ [key: string]: unknown;
32
+ }
33
+ export interface LoginResult {
34
+ /** Primary account (personal or first). */
35
+ account: LoginAccount;
36
+ /** All user accounts from login (personal + organization). Use for account selector. */
37
+ accounts: LoginAccount[];
38
+ accessToken: string;
39
+ refreshToken: string;
40
+ }
41
+ export interface OrganizationItem {
42
+ id: string;
43
+ name: string;
44
+ [key: string]: unknown;
45
+ }
46
+ export interface ProjectItem {
47
+ id: string;
48
+ name: string;
49
+ slug: string;
50
+ [key: string]: unknown;
51
+ }
52
+ /**
53
+ * Exchange API key (clientId + clientSecret) for an access token.
54
+ * POST {baseUrl}/api/auth/token
55
+ */
56
+ export declare function exchangeApiKey(baseUrl: string, body: TokenExchangeRequest): Promise<TokenExchangeResponse>;
57
+ /**
58
+ * Login with email and password. POST {baseUrl}/api/auth/login
59
+ * Returns access token, refresh token, and primary account (personal).
60
+ */
61
+ export declare function loginWithEmail(baseUrl: string, email: string, password: string): Promise<LoginResult>;
62
+ /**
63
+ * Exchange one-time CLI OAuth code for session. POST {baseUrl}/api/auth/cli-callback
64
+ * Used after browser redirect from GitHub OAuth when redirect_uri was localhost.
65
+ */
66
+ export declare function exchangeCliCallback(baseUrl: string, code: string): Promise<LoginResult>;
67
+ /**
68
+ * Fetch organizations (paginated). Requires Bearer token. GET /api/organizations?scopeId=&tenant=
69
+ * Scope is the account context (user's personal account id, tenant 'account').
70
+ */
71
+ export declare function fetchOrganizations(baseUrl: string, accessToken: string, scope: ApiScope): Promise<OrganizationItem[]>;
72
+ /**
73
+ * Fetch projects for a scope (account or organization). Paginated. Requires Bearer token.
74
+ * GET /api/projects?scopeId=&tenant=
75
+ */
76
+ export declare function fetchProjects(baseUrl: string, accessToken: string, scope: ApiScope): Promise<ProjectItem[]>;
77
+ export interface ApiScope {
78
+ id: string;
79
+ tenant: string;
80
+ }
81
+ export interface ResourceItem {
82
+ id: string;
83
+ slug: string;
84
+ name: string;
85
+ actions: string[];
86
+ [key: string]: unknown;
87
+ }
88
+ export interface PermissionItem {
89
+ id: string;
90
+ action: string;
91
+ name: string;
92
+ [key: string]: unknown;
93
+ }
94
+ /**
95
+ * Fetch all resources for a scope (paginated). Requires Bearer token.
96
+ */
97
+ export declare function fetchResources(baseUrl: string, accessToken: string, scope: ApiScope): Promise<ResourceItem[]>;
98
+ /**
99
+ * Fetch all permissions for a scope (paginated). Requires Bearer token.
100
+ */
101
+ export declare function fetchPermissions(baseUrl: string, accessToken: string, scope: ApiScope): Promise<PermissionItem[]>;
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createConfigCommand(program: Command): void;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Generate TypeScript content for ResourceSlug and ResourceAction from project data.
3
+ * Mirrors the shape of @grantjs/constants permissions/resources.ts.
4
+ */
5
+ /** Convert slug (e.g. "user-documents") or action (e.g. "Create") to PascalCase key. */
6
+ export declare function toPascalCase(s: string): string;
7
+ /**
8
+ * Generate the TypeScript file content for ResourceSlug and ResourceAction.
9
+ * - slugs: unique resource slugs from the project (e.g. from GET /api/resources).
10
+ * - actions: unique permission actions from the project (e.g. from GET /api/permissions).
11
+ */
12
+ export declare function generateTypesContent(slugs: string[], actions: string[]): string;
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createGenerateTypesCommand(program: Command): void;
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createStartCommand(program: Command): void;
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createVersionCommand(program: Command): void;
@@ -0,0 +1,3 @@
1
+ export { DEFAULT_PROFILE_NAME, getConfigDir, getConfigPath, getProfileConfig, listProfileNames, loadConfig, loadConfigFile, loadProfile, resolveProfileName, saveConfigFile, } from './storage.js';
2
+ export { resolveAccessToken } from './resolve-token.js';
3
+ export type { ApiKeyCredentials, GrantConfig, GrantConfigFile, GrantScope, ProfileName, SessionCredentials, } from '../types/config.js';
@@ -0,0 +1,9 @@
1
+ import { GrantConfig } from '../types/config.js';
2
+ /**
3
+ * Resolve a valid access token from stored config.
4
+ * Only tokens are stored (no credentials). Session auth does not auto-refresh; user must re-auth when the access token expires.
5
+ *
6
+ * - API key: exchanges clientId + clientSecret for a fresh token (no credentials stored after exchange).
7
+ * - Session: returns the stored access token. When it expires, the user must run "grant start" again to re-authenticate.
8
+ */
9
+ export declare function resolveAccessToken(config: GrantConfig): Promise<string>;
@@ -0,0 +1,49 @@
1
+ import { GrantConfig, GrantConfigFile } from '../types/config.js';
2
+ declare const DEFAULT_PROFILE_NAME = "default";
3
+ /**
4
+ * Returns the platform-specific config directory for Grant CLI.
5
+ * - Windows: %APPDATA%\grant
6
+ * - Linux/macOS: $XDG_CONFIG_HOME/grant or ~/.config/grant
7
+ */
8
+ export declare function getConfigDir(): string;
9
+ /**
10
+ * Returns the path to the config file (config dir + config.json).
11
+ */
12
+ export declare function getConfigPath(): string;
13
+ /**
14
+ * Load config file from disk. Migrates legacy single-config to profiles shape.
15
+ * Returns null if file does not exist or is invalid.
16
+ */
17
+ export declare function loadConfigFile(): Promise<GrantConfigFile | null>;
18
+ /**
19
+ * Save config file to disk. Creates config dir if needed. Sets file mode to 0o600 (owner read/write only).
20
+ */
21
+ export declare function saveConfigFile(file: GrantConfigFile): Promise<void>;
22
+ /**
23
+ * Resolve which profile name to use: explicit name, or file's default, or "default".
24
+ */
25
+ export declare function resolveProfileName(file: GrantConfigFile, profileFlag: string | undefined): string;
26
+ /**
27
+ * Get config for a profile. Returns null if profile does not exist.
28
+ */
29
+ export declare function getProfileConfig(file: GrantConfigFile, profileName: string): GrantConfig | null;
30
+ /**
31
+ * List profile names. Returns empty array if no file.
32
+ */
33
+ export declare function listProfileNames(file: GrantConfigFile | null): string[];
34
+ /** Default profile name constant for use in prompts/help. */
35
+ export { DEFAULT_PROFILE_NAME };
36
+ /**
37
+ * Load config file and return the default profile's config.
38
+ * Convenience for callers that only need one profile (default). Returns null if no file or default profile missing.
39
+ */
40
+ export declare function loadConfig(): Promise<GrantConfig | null>;
41
+ /**
42
+ * Load config file and return the resolved profile's config plus file and name.
43
+ * Use when you need to read and then update (save) the file. Returns null if no file or profile does not exist.
44
+ */
45
+ export declare function loadProfile(profileFlag?: string): Promise<{
46
+ file: GrantConfigFile;
47
+ config: GrantConfig;
48
+ profileName: string;
49
+ } | null>;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};