@constructive-io/cli 6.0.4 → 6.1.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.
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Configuration manager for the CNC execution engine
3
+ * Uses appstash for directory resolution
4
+ */
5
+ import type { ContextConfig, GlobalSettings, Credentials, ContextCredentials } from './types';
6
+ /**
7
+ * Get the appstash directories for cnc
8
+ */
9
+ export declare function getAppDirs(): import("appstash").AppStashResult;
10
+ /**
11
+ * Load global settings
12
+ */
13
+ export declare function loadSettings(): GlobalSettings;
14
+ /**
15
+ * Save global settings
16
+ */
17
+ export declare function saveSettings(settings: GlobalSettings): void;
18
+ /**
19
+ * Load credentials
20
+ */
21
+ export declare function loadCredentials(): Credentials;
22
+ /**
23
+ * Save credentials
24
+ */
25
+ export declare function saveCredentials(credentials: Credentials): void;
26
+ /**
27
+ * Load a context configuration
28
+ */
29
+ export declare function loadContext(contextName: string): ContextConfig | null;
30
+ /**
31
+ * Save a context configuration
32
+ */
33
+ export declare function saveContext(context: ContextConfig): void;
34
+ /**
35
+ * Delete a context configuration
36
+ */
37
+ export declare function deleteContext(contextName: string): boolean;
38
+ /**
39
+ * List all context configurations
40
+ */
41
+ export declare function listContexts(): ContextConfig[];
42
+ /**
43
+ * Get the current active context
44
+ */
45
+ export declare function getCurrentContext(): ContextConfig | null;
46
+ /**
47
+ * Set the current active context
48
+ */
49
+ export declare function setCurrentContext(contextName: string): boolean;
50
+ /**
51
+ * Create a new context configuration
52
+ */
53
+ export declare function createContext(name: string, endpoint: string): ContextConfig;
54
+ /**
55
+ * Get credentials for a context
56
+ */
57
+ export declare function getContextCredentials(contextName: string): ContextCredentials | null;
58
+ /**
59
+ * Set credentials for a context
60
+ */
61
+ export declare function setContextCredentials(contextName: string, token: string, options?: {
62
+ expiresAt?: string;
63
+ refreshToken?: string;
64
+ }): void;
65
+ /**
66
+ * Remove credentials for a context
67
+ */
68
+ export declare function removeContextCredentials(contextName: string): boolean;
69
+ /**
70
+ * Check if a context has valid credentials
71
+ */
72
+ export declare function hasValidCredentials(contextName: string): boolean;
@@ -0,0 +1,280 @@
1
+ "use strict";
2
+ /**
3
+ * Configuration manager for the CNC execution engine
4
+ * Uses appstash for directory resolution
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.getAppDirs = getAppDirs;
41
+ exports.loadSettings = loadSettings;
42
+ exports.saveSettings = saveSettings;
43
+ exports.loadCredentials = loadCredentials;
44
+ exports.saveCredentials = saveCredentials;
45
+ exports.loadContext = loadContext;
46
+ exports.saveContext = saveContext;
47
+ exports.deleteContext = deleteContext;
48
+ exports.listContexts = listContexts;
49
+ exports.getCurrentContext = getCurrentContext;
50
+ exports.setCurrentContext = setCurrentContext;
51
+ exports.createContext = createContext;
52
+ exports.getContextCredentials = getContextCredentials;
53
+ exports.setContextCredentials = setContextCredentials;
54
+ exports.removeContextCredentials = removeContextCredentials;
55
+ exports.hasValidCredentials = hasValidCredentials;
56
+ const fs = __importStar(require("fs"));
57
+ const path = __importStar(require("path"));
58
+ const appstash_1 = require("appstash");
59
+ const types_1 = require("./types");
60
+ const TOOL_NAME = 'cnc';
61
+ /**
62
+ * Get the appstash directories for cnc
63
+ */
64
+ function getAppDirs() {
65
+ return (0, appstash_1.appstash)(TOOL_NAME, { ensure: true });
66
+ }
67
+ /**
68
+ * Get path to a config file
69
+ */
70
+ function getConfigPath(filename) {
71
+ const dirs = getAppDirs();
72
+ return (0, appstash_1.resolve)(dirs, 'config', filename);
73
+ }
74
+ /**
75
+ * Get path to a context config file
76
+ */
77
+ function getContextConfigPath(contextName) {
78
+ const dirs = getAppDirs();
79
+ const contextsDir = (0, appstash_1.resolve)(dirs, 'config', 'contexts');
80
+ if (!fs.existsSync(contextsDir)) {
81
+ fs.mkdirSync(contextsDir, { recursive: true });
82
+ }
83
+ return path.join(contextsDir, `${contextName}.json`);
84
+ }
85
+ /**
86
+ * Load global settings
87
+ */
88
+ function loadSettings() {
89
+ const settingsPath = getConfigPath('settings.json');
90
+ if (fs.existsSync(settingsPath)) {
91
+ try {
92
+ const content = fs.readFileSync(settingsPath, 'utf8');
93
+ return { ...types_1.DEFAULT_SETTINGS, ...JSON.parse(content) };
94
+ }
95
+ catch {
96
+ return types_1.DEFAULT_SETTINGS;
97
+ }
98
+ }
99
+ return types_1.DEFAULT_SETTINGS;
100
+ }
101
+ /**
102
+ * Save global settings
103
+ */
104
+ function saveSettings(settings) {
105
+ const settingsPath = getConfigPath('settings.json');
106
+ const configDir = path.dirname(settingsPath);
107
+ if (!fs.existsSync(configDir)) {
108
+ fs.mkdirSync(configDir, { recursive: true });
109
+ }
110
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
111
+ }
112
+ /**
113
+ * Load credentials
114
+ */
115
+ function loadCredentials() {
116
+ const credentialsPath = getConfigPath('credentials.json');
117
+ if (fs.existsSync(credentialsPath)) {
118
+ try {
119
+ const content = fs.readFileSync(credentialsPath, 'utf8');
120
+ return JSON.parse(content);
121
+ }
122
+ catch {
123
+ return { tokens: {} };
124
+ }
125
+ }
126
+ return { tokens: {} };
127
+ }
128
+ /**
129
+ * Save credentials
130
+ */
131
+ function saveCredentials(credentials) {
132
+ const credentialsPath = getConfigPath('credentials.json');
133
+ const configDir = path.dirname(credentialsPath);
134
+ if (!fs.existsSync(configDir)) {
135
+ fs.mkdirSync(configDir, { recursive: true });
136
+ }
137
+ fs.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), {
138
+ mode: 0o600, // Read/write for owner only
139
+ });
140
+ }
141
+ /**
142
+ * Load a context configuration
143
+ */
144
+ function loadContext(contextName) {
145
+ const contextPath = getContextConfigPath(contextName);
146
+ if (fs.existsSync(contextPath)) {
147
+ try {
148
+ const content = fs.readFileSync(contextPath, 'utf8');
149
+ return JSON.parse(content);
150
+ }
151
+ catch {
152
+ return null;
153
+ }
154
+ }
155
+ return null;
156
+ }
157
+ /**
158
+ * Save a context configuration
159
+ */
160
+ function saveContext(context) {
161
+ const contextPath = getContextConfigPath(context.name);
162
+ fs.writeFileSync(contextPath, JSON.stringify(context, null, 2));
163
+ }
164
+ /**
165
+ * Delete a context configuration
166
+ */
167
+ function deleteContext(contextName) {
168
+ const contextPath = getContextConfigPath(contextName);
169
+ if (fs.existsSync(contextPath)) {
170
+ fs.unlinkSync(contextPath);
171
+ return true;
172
+ }
173
+ return false;
174
+ }
175
+ /**
176
+ * List all context configurations
177
+ */
178
+ function listContexts() {
179
+ const dirs = getAppDirs();
180
+ const contextsDir = (0, appstash_1.resolve)(dirs, 'config', 'contexts');
181
+ if (!fs.existsSync(contextsDir)) {
182
+ return [];
183
+ }
184
+ const files = fs.readdirSync(contextsDir).filter(f => f.endsWith('.json'));
185
+ const contexts = [];
186
+ for (const file of files) {
187
+ try {
188
+ const content = fs.readFileSync(path.join(contextsDir, file), 'utf8');
189
+ contexts.push(JSON.parse(content));
190
+ }
191
+ catch {
192
+ // Skip invalid files
193
+ }
194
+ }
195
+ return contexts;
196
+ }
197
+ /**
198
+ * Get the current active context
199
+ */
200
+ function getCurrentContext() {
201
+ const settings = loadSettings();
202
+ if (settings.currentContext) {
203
+ return loadContext(settings.currentContext);
204
+ }
205
+ return null;
206
+ }
207
+ /**
208
+ * Set the current active context
209
+ */
210
+ function setCurrentContext(contextName) {
211
+ const context = loadContext(contextName);
212
+ if (!context) {
213
+ return false;
214
+ }
215
+ const settings = loadSettings();
216
+ settings.currentContext = contextName;
217
+ saveSettings(settings);
218
+ return true;
219
+ }
220
+ /**
221
+ * Create a new context configuration
222
+ */
223
+ function createContext(name, endpoint) {
224
+ const now = new Date().toISOString();
225
+ const context = {
226
+ name,
227
+ endpoint,
228
+ createdAt: now,
229
+ updatedAt: now,
230
+ };
231
+ saveContext(context);
232
+ return context;
233
+ }
234
+ /**
235
+ * Get credentials for a context
236
+ */
237
+ function getContextCredentials(contextName) {
238
+ const credentials = loadCredentials();
239
+ return credentials.tokens[contextName] || null;
240
+ }
241
+ /**
242
+ * Set credentials for a context
243
+ */
244
+ function setContextCredentials(contextName, token, options) {
245
+ const credentials = loadCredentials();
246
+ credentials.tokens[contextName] = {
247
+ token,
248
+ expiresAt: options?.expiresAt,
249
+ refreshToken: options?.refreshToken,
250
+ };
251
+ saveCredentials(credentials);
252
+ }
253
+ /**
254
+ * Remove credentials for a context
255
+ */
256
+ function removeContextCredentials(contextName) {
257
+ const credentials = loadCredentials();
258
+ if (credentials.tokens[contextName]) {
259
+ delete credentials.tokens[contextName];
260
+ saveCredentials(credentials);
261
+ return true;
262
+ }
263
+ return false;
264
+ }
265
+ /**
266
+ * Check if a context has valid credentials
267
+ */
268
+ function hasValidCredentials(contextName) {
269
+ const creds = getContextCredentials(contextName);
270
+ if (!creds || !creds.token) {
271
+ return false;
272
+ }
273
+ if (creds.expiresAt) {
274
+ const expiresAt = new Date(creds.expiresAt);
275
+ if (expiresAt <= new Date()) {
276
+ return false;
277
+ }
278
+ }
279
+ return true;
280
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Config module exports
3
+ */
4
+ export * from './types';
5
+ export * from './config-manager';
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ /**
3
+ * Config module exports
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
17
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ __exportStar(require("./types"), exports);
21
+ __exportStar(require("./config-manager"), exports);
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Configuration types for the CNC execution engine
3
+ */
4
+ /**
5
+ * Context configuration stored in ~/.cnc/config/contexts/{name}.json
6
+ * Similar to kubectl contexts - bundles endpoint + credentials
7
+ */
8
+ export interface ContextConfig {
9
+ /** Context name (used as identifier) */
10
+ name: string;
11
+ /** GraphQL endpoint URL */
12
+ endpoint: string;
13
+ /** Created timestamp */
14
+ createdAt: string;
15
+ /** Last updated timestamp */
16
+ updatedAt: string;
17
+ }
18
+ /**
19
+ * Global settings stored in ~/.cnc/config/settings.json
20
+ */
21
+ export interface GlobalSettings {
22
+ /** Currently active context name */
23
+ currentContext?: string;
24
+ }
25
+ /**
26
+ * Credentials stored in ~/.cnc/config/credentials.json
27
+ */
28
+ export interface Credentials {
29
+ /** API tokens per context */
30
+ tokens: Record<string, ContextCredentials>;
31
+ }
32
+ /**
33
+ * Per-context credentials
34
+ */
35
+ export interface ContextCredentials {
36
+ /** Bearer token for API authentication */
37
+ token: string;
38
+ /** Token expiration timestamp (ISO string) */
39
+ expiresAt?: string;
40
+ /** Refresh token if available */
41
+ refreshToken?: string;
42
+ }
43
+ /**
44
+ * Default global settings
45
+ */
46
+ export declare const DEFAULT_SETTINGS: GlobalSettings;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ /**
3
+ * Configuration types for the CNC execution engine
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DEFAULT_SETTINGS = void 0;
7
+ /**
8
+ * Default global settings
9
+ */
10
+ exports.DEFAULT_SETTINGS = {};
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Authentication commands for the CNC execution engine
3
+ */
4
+ import { extractFirst } from 'inquirerer';
5
+ import chalk from 'yanse';
6
+ import { getCurrentContext, loadContext, listContexts, getContextCredentials, setContextCredentials, removeContextCredentials, hasValidCredentials, loadSettings, } from '../config';
7
+ const usage = `
8
+ Constructive Authentication:
9
+
10
+ cnc auth <command> [OPTIONS]
11
+
12
+ Commands:
13
+ set-token <token> Set API token for the current context
14
+ status Show authentication status
15
+ logout Remove credentials for the current context
16
+
17
+ Options:
18
+ --context <name> Specify context (defaults to current context)
19
+ --expires <date> Token expiration date (ISO format)
20
+
21
+ Examples:
22
+ cnc auth set-token eyJhbGciOiJIUzI1NiIs...
23
+ cnc auth status
24
+ cnc auth logout
25
+ cnc auth set-token <token> --context my-api
26
+
27
+ --help, -h Show this help message
28
+ `;
29
+ export default async (argv, prompter, _options) => {
30
+ if (argv.help || argv.h) {
31
+ console.log(usage);
32
+ process.exit(0);
33
+ }
34
+ const { first: subcommand, newArgv } = extractFirst(argv);
35
+ if (!subcommand) {
36
+ const answer = await prompter.prompt(argv, [
37
+ {
38
+ type: 'autocomplete',
39
+ name: 'subcommand',
40
+ message: 'What do you want to do?',
41
+ options: ['set-token', 'status', 'logout'],
42
+ },
43
+ ]);
44
+ return handleSubcommand(answer.subcommand, newArgv, prompter);
45
+ }
46
+ return handleSubcommand(subcommand, newArgv, prompter);
47
+ };
48
+ async function handleSubcommand(subcommand, argv, prompter) {
49
+ switch (subcommand) {
50
+ case 'set-token':
51
+ return handleSetToken(argv, prompter);
52
+ case 'status':
53
+ return handleStatus(argv);
54
+ case 'logout':
55
+ return handleLogout(argv, prompter);
56
+ default:
57
+ console.log(usage);
58
+ console.error(chalk.red(`Unknown subcommand: ${subcommand}`));
59
+ process.exit(1);
60
+ }
61
+ }
62
+ async function getTargetContext(argv, prompter) {
63
+ if (argv.context && typeof argv.context === 'string') {
64
+ const context = loadContext(argv.context);
65
+ if (!context) {
66
+ console.error(chalk.red(`Context "${argv.context}" not found.`));
67
+ process.exit(1);
68
+ }
69
+ return argv.context;
70
+ }
71
+ const current = getCurrentContext();
72
+ if (current) {
73
+ return current.name;
74
+ }
75
+ const contexts = listContexts();
76
+ if (contexts.length === 0) {
77
+ console.error(chalk.red('No contexts configured.'));
78
+ console.log(chalk.gray('Run "cnc context create <name>" to create one first.'));
79
+ process.exit(1);
80
+ }
81
+ const answer = await prompter.prompt(argv, [
82
+ {
83
+ type: 'autocomplete',
84
+ name: 'context',
85
+ message: 'Select context',
86
+ options: contexts.map(c => c.name),
87
+ },
88
+ ]);
89
+ return answer.context;
90
+ }
91
+ async function handleSetToken(argv, prompter) {
92
+ const contextName = await getTargetContext(argv, prompter);
93
+ const { first: token, newArgv } = extractFirst(argv);
94
+ let tokenValue = token;
95
+ if (!tokenValue) {
96
+ const answer = await prompter.prompt(newArgv, [
97
+ {
98
+ type: 'password',
99
+ name: 'token',
100
+ message: 'API Token',
101
+ required: true,
102
+ },
103
+ ]);
104
+ tokenValue = answer.token;
105
+ }
106
+ if (!tokenValue || tokenValue.trim() === '') {
107
+ console.error(chalk.red('Token cannot be empty.'));
108
+ process.exit(1);
109
+ }
110
+ const expiresAt = argv.expires;
111
+ setContextCredentials(contextName, tokenValue.trim(), { expiresAt });
112
+ console.log(chalk.green(`Token saved for context: ${contextName}`));
113
+ if (expiresAt) {
114
+ console.log(chalk.gray(`Expires: ${expiresAt}`));
115
+ }
116
+ }
117
+ function handleStatus(argv) {
118
+ const settings = loadSettings();
119
+ const contexts = listContexts();
120
+ if (contexts.length === 0) {
121
+ console.log(chalk.gray('No contexts configured.'));
122
+ return;
123
+ }
124
+ if (argv.context && typeof argv.context === 'string') {
125
+ const context = loadContext(argv.context);
126
+ if (!context) {
127
+ console.error(chalk.red(`Context "${argv.context}" not found.`));
128
+ process.exit(1);
129
+ }
130
+ showContextAuthStatus(context.name, settings.currentContext === context.name);
131
+ return;
132
+ }
133
+ console.log(chalk.bold('Authentication Status:'));
134
+ console.log();
135
+ for (const context of contexts) {
136
+ const isCurrent = context.name === settings.currentContext;
137
+ showContextAuthStatus(context.name, isCurrent);
138
+ }
139
+ }
140
+ function showContextAuthStatus(contextName, isCurrent) {
141
+ const creds = getContextCredentials(contextName);
142
+ const hasAuth = hasValidCredentials(contextName);
143
+ const marker = isCurrent ? chalk.green('*') : ' ';
144
+ console.log(`${marker} ${chalk.bold(contextName)}`);
145
+ if (hasAuth && creds) {
146
+ console.log(` Status: ${chalk.green('Authenticated')}`);
147
+ console.log(` Token: ${maskToken(creds.token)}`);
148
+ if (creds.expiresAt) {
149
+ const expiresAt = new Date(creds.expiresAt);
150
+ const now = new Date();
151
+ if (expiresAt <= now) {
152
+ console.log(` Expires: ${chalk.red(creds.expiresAt + ' (expired)')}`);
153
+ }
154
+ else {
155
+ console.log(` Expires: ${creds.expiresAt}`);
156
+ }
157
+ }
158
+ }
159
+ else if (creds && creds.token) {
160
+ console.log(` Status: ${chalk.red('Expired')}`);
161
+ console.log(` Token: ${maskToken(creds.token)}`);
162
+ if (creds.expiresAt) {
163
+ console.log(` Expired: ${creds.expiresAt}`);
164
+ }
165
+ }
166
+ else {
167
+ console.log(` Status: ${chalk.yellow('Not authenticated')}`);
168
+ }
169
+ console.log();
170
+ }
171
+ function maskToken(token) {
172
+ if (token.length <= 10) {
173
+ return '****';
174
+ }
175
+ return token.substring(0, 6) + '...' + token.substring(token.length - 4);
176
+ }
177
+ async function handleLogout(argv, prompter) {
178
+ const contextName = await getTargetContext(argv, prompter);
179
+ const creds = getContextCredentials(contextName);
180
+ if (!creds) {
181
+ console.log(chalk.gray(`No credentials found for context: ${contextName}`));
182
+ return;
183
+ }
184
+ const confirm = await prompter.prompt(argv, [
185
+ {
186
+ type: 'confirm',
187
+ name: 'confirm',
188
+ message: `Remove credentials for context "${contextName}"?`,
189
+ default: false,
190
+ },
191
+ ]);
192
+ if (!confirm.confirm) {
193
+ console.log(chalk.gray('Cancelled.'));
194
+ return;
195
+ }
196
+ if (removeContextCredentials(contextName)) {
197
+ console.log(chalk.green(`Credentials removed for context: ${contextName}`));
198
+ }
199
+ else {
200
+ console.log(chalk.gray(`No credentials found for context: ${contextName}`));
201
+ }
202
+ }
@@ -18,6 +18,8 @@ Generator Options:
18
18
  --orm Generate ORM client
19
19
  --output <dir> Output directory (default: codegen)
20
20
  --authorization <token> Authorization header value
21
+ --browserCompatible Generate browser-compatible code (default: true)
22
+ Set to false for Node.js with localhost DNS fix
21
23
  --dryRun Preview without writing files
22
24
  --verbose Verbose output
23
25
 
@@ -51,6 +53,7 @@ export default async (argv, prompter, _options) => {
51
53
  authorization: answers.authorization,
52
54
  reactQuery: answers.reactQuery,
53
55
  orm: answers.orm,
56
+ browserCompatible: answers.browserCompatible,
54
57
  dryRun: answers.dryRun,
55
58
  verbose: answers.verbose,
56
59
  });