@factorialco/fcode-cli 1.1.1 → 1.2.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
@@ -20,7 +20,7 @@ $ npm install -g @factorialco/fcode-cli
20
20
  $ fcode COMMAND
21
21
  running command...
22
22
  $ fcode (--version)
23
- @factorialco/fcode-cli/1.1.1 linux-x64 node-v22.22.3
23
+ @factorialco/fcode-cli/1.2.0 linux-x64 node-v22.22.3
24
24
  $ fcode --help [COMMAND]
25
25
  USAGE
26
26
  $ fcode COMMAND
@@ -587,7 +587,14 @@ const main = typeof _hooksModule['${fn}'] === 'function'
587
587
  console.log('[fcode] variables.delete removed ' + key + ' from variables.local.env (local run).');
588
588
  }
589
589
  };
590
- })()
590
+ })(),
591
+ sendMail: async ({ to, subject, text, html } = {}) => {
592
+ // No manager locally: log the email contents instead of sending, so
593
+ // devs can see exactly what would be delivered when run in the cloud.
594
+ console.log('[fcode] sendMail (local run) — not sent, logging contents below:');
595
+ console.log(JSON.stringify({ to, subject, text, html }, null, 2));
596
+ return { messageId: 'local-run', accepted: [].concat(to || []), rejected: [] };
597
+ }
591
598
  };
592
599
  ${Object.entries(this.getVariablesObject())
593
600
  .map(([key, value]) => `process.env['${key}'] = process.env['${key}'] ?? ${JSON.stringify(value)}`)
package/dist/env.d.ts ADDED
@@ -0,0 +1,43 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Validated environment schema for the fcode CLI. This is the single source of truth
4
+ * for the environment variables the CLI reads — add new vars here.
5
+ *
6
+ * Unlike the long-running services (storage-api, ai), a CLI reads config lazily and
7
+ * different commands need different vars, so there is currently NO universally required
8
+ * variable: every entry is optional or carries a default that preserves prior behavior.
9
+ * The fail-fast (❌ + exit 1) path is kept for parity with the services and so that a
10
+ * genuinely-required var added later fails at process start listing every problem.
11
+ */
12
+ export declare const envSchema: z.ZodObject<{
13
+ FCODE_CLOUD_URL: z.ZodDefault<z.ZodString>;
14
+ FCODE_AUTH_URL: z.ZodOptional<z.ZodString>;
15
+ FCODE_CLIENT_ID: z.ZodDefault<z.ZodString>;
16
+ FCODE_CONFIG_PATH: z.ZodOptional<z.ZodString>;
17
+ IGNORE_WORKSPACE_NOT_EMPTY: z.ZodOptional<z.ZodEnum<{
18
+ true: "true";
19
+ false: "false";
20
+ }>>;
21
+ FCODE_DISABLE_UPDATE_NOTIFIER: z.ZodOptional<z.ZodEnum<{
22
+ true: "true";
23
+ false: "false";
24
+ }>>;
25
+ SILENT_MODE: z.ZodOptional<z.ZodEnum<{
26
+ true: "true";
27
+ false: "false";
28
+ }>>;
29
+ CLI_UX_SKIP_TTY_CHECK: z.ZodOptional<z.ZodString>;
30
+ YC_COMMAND: z.ZodOptional<z.ZodString>;
31
+ }, z.core.$strip>;
32
+ export declare const env: {
33
+ FCODE_CLOUD_URL: string;
34
+ FCODE_CLIENT_ID: string;
35
+ FCODE_AUTH_URL?: string | undefined;
36
+ FCODE_CONFIG_PATH?: string | undefined;
37
+ IGNORE_WORKSPACE_NOT_EMPTY?: "true" | "false" | undefined;
38
+ FCODE_DISABLE_UPDATE_NOTIFIER?: "true" | "false" | undefined;
39
+ SILENT_MODE?: "true" | "false" | undefined;
40
+ CLI_UX_SKIP_TTY_CHECK?: string | undefined;
41
+ YC_COMMAND?: string | undefined;
42
+ };
43
+ export default env;
package/dist/env.js ADDED
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.env = exports.envSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ /**
6
+ * Validated environment schema for the fcode CLI. This is the single source of truth
7
+ * for the environment variables the CLI reads — add new vars here.
8
+ *
9
+ * Unlike the long-running services (storage-api, ai), a CLI reads config lazily and
10
+ * different commands need different vars, so there is currently NO universally required
11
+ * variable: every entry is optional or carries a default that preserves prior behavior.
12
+ * The fail-fast (❌ + exit 1) path is kept for parity with the services and so that a
13
+ * genuinely-required var added later fails at process start listing every problem.
14
+ */
15
+ exports.envSchema = zod_1.z.object({
16
+ // --- Cloud / auth endpoints ---
17
+ FCODE_CLOUD_URL: zod_1.z.string().min(1).default('https://code.factorial.dev/platform'),
18
+ FCODE_AUTH_URL: zod_1.z.string().min(1).optional(),
19
+ FCODE_CLIENT_ID: zod_1.z.string().min(1).default('cli'),
20
+ // --- Local config location (defaults to os.homedir() at the call site) ---
21
+ FCODE_CONFIG_PATH: zod_1.z.string().min(1).optional(),
22
+ // --- Behavior flags (compared as the strings 'true' / 'false' at the call sites) ---
23
+ IGNORE_WORKSPACE_NOT_EMPTY: zod_1.z.enum(['true', 'false']).optional(),
24
+ FCODE_DISABLE_UPDATE_NOTIFIER: zod_1.z.enum(['true', 'false']).optional(),
25
+ SILENT_MODE: zod_1.z.enum(['true', 'false']).optional(),
26
+ CLI_UX_SKIP_TTY_CHECK: zod_1.z.string().min(1).optional(),
27
+ // --- Debug tooling ---
28
+ YC_COMMAND: zod_1.z.string().min(1).optional(),
29
+ });
30
+ /** Treat empty strings (e.g. an unset shell/dotenv var rendered as "") as missing. */
31
+ const raw = Object.fromEntries(Object.entries(process.env).filter(([, value]) => value !== ''));
32
+ /**
33
+ * Skip the hard fail-fast under test runners and when explicitly opted out, so importing
34
+ * this module never kills a test worker. Real invocations always validate.
35
+ */
36
+ const skipHardExit = Boolean(process.env.VITEST) || process.env.SKIP_ENV_VALIDATION === 'true';
37
+ const result = exports.envSchema.safeParse(raw);
38
+ let parsed;
39
+ if (result.success) {
40
+ parsed = result.data;
41
+ }
42
+ else if (skipHardExit) {
43
+ // No universally-required vars today, so parsing `raw` alone succeeds; this branch stays
44
+ // for parity with the services if a required var is ever added.
45
+ parsed = exports.envSchema.parse(raw);
46
+ }
47
+ else {
48
+ const issues = result.error.issues.map((issue) => ` - ${issue.path.join('.') || '(root)'}: ${issue.message}`).join('\n');
49
+ process.stderr.write(`❌ Invalid environment variables:\n${issues}\n`);
50
+ process.exit(1);
51
+ }
52
+ exports.env = parsed;
53
+ exports.default = exports.env;
@@ -20,14 +20,12 @@ const updateNotifier = async function ({ config, id }) {
20
20
  await globalConfig.set('versionCheckTime', now);
21
21
  if (config.version !== lastestVersion) {
22
22
  console.log(`
23
- ${color_1.default.cyan(`__ __ _____ _
24
- \\ \\ / / / ____| | |
25
- \\ \\_/ /__ _ __ | | ___ __| | ___
26
- \\ / _ \\ '_ \\| | / _ \\ / _\` |/ _ \\
27
- | | __/ |_) | |___| (_) | (_| | __/
28
- |_|\\___| .__/ \\_____\\___/ \\__,_|\\___|
29
- | |
30
- |_|`)}
23
+ ${color_1.default.cyan(` ______ _ _ _ _____ _
24
+ | ____| | | (_) | |/ ____| | |
25
+ | |__ __ _ ___| |_ ___ _ __ _ __ _| | | ___ __| | ___
26
+ | __/ _\` |/ __| __/ _ \\| '__| |/ _\` | | | / _ \\ / _\` |/ _ \\
27
+ | | | (_| | (__| || (_) | | | | (_| | | |___| (_) | (_| | __/
28
+ |_| \\__,_|\\___|\\__\\___/|_| |_|\\__,_|_|\\_____\\___/ \\__,_|\\___|`)}
31
29
 
32
30
  Update available ${color_1.default.red(config.version)} → ${color_1.default.greenBright(lastestVersion)}
33
31
  Run ${color_1.default.bold('`npm i -g @factorialco/fcode-cli`')} to update
@@ -90,6 +90,25 @@ class FcodeGlobal:
90
90
  self.variables = variables
91
91
  self.processes = Processes()
92
92
 
93
+ def send_mail(self, to=None, subject=None, text=None, html=None):
94
+ # No manager locally: log the email contents instead of sending, so devs
95
+ # can see exactly what would be delivered when run in the cloud.
96
+ import json
97
+ import logging
98
+
99
+ logger = logging.getLogger("fcode_logger")
100
+ logger.info(
101
+ "[fcode] send_mail (local run) — not sent, logging contents below:"
102
+ )
103
+ logger.info(
104
+ json.dumps(
105
+ {"to": to, "subject": subject, "text": text, "html": html},
106
+ indent=2,
107
+ )
108
+ )
109
+ accepted = to if isinstance(to, list) else ([to] if to else [])
110
+ return {"messageId": "local-run", "accepted": accepted, "rejected": []}
111
+
93
112
  def import_module(self, module_name, version=None):
94
113
  module_path = module_name
95
114
  if version:
@@ -1779,5 +1779,5 @@
1779
1779
  ]
1780
1780
  }
1781
1781
  },
1782
- "version": "1.1.1"
1782
+ "version": "1.2.0"
1783
1783
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@factorialco/fcode-cli",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "packageManager": "pnpm@11.7.0",
5
5
  "description": "Factorial Code Command Line Interface",
6
6
  "homepage": "https://code.factorialhr.com/docs/cli/",
@@ -40,7 +40,8 @@
40
40
  "open": "^8.4.2",
41
41
  "openid-client": "^5.6.5",
42
42
  "string-width": "^4.2.3",
43
- "strip-comments": "^2.0.1"
43
+ "strip-comments": "^2.0.1",
44
+ "zod": "^4.4.3"
44
45
  },
45
46
  "peerDependencies": {
46
47
  "graphql-request": "^7.4.0",