free-coding-models 0.3.42 → 0.3.44

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/src/telemetry.js CHANGED
@@ -7,7 +7,8 @@
7
7
  * All telemetry is strictly opt-in-by-default, fire-and-forget, and anonymous:
8
8
  * - A stable `anonymousId` (UUID prefixed with "anon_") is generated once and stored
9
9
  * in ~/.free-coding-models.json. No personal data is ever collected.
10
- * - PostHog is used for product analytics (app_start events, mode, platform).
10
+ * - PostHog is used for product analytics (`app_start`, `app_use`, and lightweight
11
+ * `app_action` events covering launches and key product actions).
11
12
  * - Discord webhooks carry anonymous feature requests (J key) and bug reports (I key).
12
13
  * - `isTelemetryEnabled()` checks: CLI flag → env var → default (enabled).
13
14
  * - `telemetryDebug()` writes to stderr only when FREE_CODING_MODELS_TELEMETRY_DEBUG=1.
@@ -29,6 +30,7 @@
29
30
  * → getTelemetrySystem() — Convert platform to human label
30
31
  * → getTelemetryTerminal() — Infer terminal family from env hints
31
32
  * → isTelemetryEnabled(config, cliArgs) — Resolve effective enabled state
33
+ * → buildTelemetryProperties(payload) — Build sanitized PostHog event properties
32
34
  * → sendUsageTelemetry(config, cliArgs, payload)— Fire-and-forget PostHog ping
33
35
  * → sendFeatureRequest(message) — Post anonymous feature request to Discord
34
36
  * → sendBugReport(message) — Post anonymous bug report to Discord
@@ -37,7 +39,7 @@
37
39
  * parseTelemetryEnv, isTelemetryDebugEnabled, telemetryDebug,
38
40
  * ensureTelemetryConfig, getTelemetryDistinctId,
39
41
  * getTelemetrySystem, getTelemetryTerminal,
40
- * isTelemetryEnabled, sendUsageTelemetry,
42
+ * isTelemetryEnabled, buildTelemetryProperties, sendUsageTelemetry,
41
43
  * sendFeatureRequest, sendBugReport
42
44
  *
43
45
  * @see src/config.js — saveConfig is imported here to persist the generated anonymousId
@@ -205,11 +207,37 @@ export function isTelemetryEnabled(config, cliArgs) {
205
207
  return true
206
208
  }
207
209
 
210
+ /**
211
+ * 📖 Build the final analytics properties object while keeping the base schema
212
+ * 📖 stable and stripping undefined values from optional action-specific fields.
213
+ * @param {{ version?: string, mode?: string, properties?: Record<string, unknown> } | undefined} payload
214
+ * @returns {Record<string, unknown>}
215
+ */
216
+ export function buildTelemetryProperties(payload) {
217
+ const extraProperties = payload?.properties && typeof payload.properties === 'object' && !Array.isArray(payload.properties)
218
+ ? payload.properties
219
+ : {}
220
+
221
+ const merged = {
222
+ ...extraProperties,
223
+ $process_person_profile: false,
224
+ source: 'cli',
225
+ app: 'free-coding-models',
226
+ version: payload?.version || LOCAL_VERSION,
227
+ app_version: payload?.version || LOCAL_VERSION,
228
+ mode: payload?.mode || 'opencode',
229
+ system: getTelemetrySystem(),
230
+ terminal: getTelemetryTerminal(),
231
+ }
232
+
233
+ return Object.fromEntries(Object.entries(merged).filter(([, value]) => value !== undefined))
234
+ }
235
+
208
236
  /**
209
237
  * 📖 Fire-and-forget analytics ping: never blocks UX, never throws.
210
238
  * @param {Record<string, unknown>} config
211
239
  * @param {{ noTelemetry?: boolean }} cliArgs
212
- * @param {{ event?: string, version?: string, mode?: string, ts?: string }} payload
240
+ * @param {{ event?: string, version?: string, mode?: string, ts?: string, properties?: Record<string, unknown> }} payload
213
241
  */
214
242
  export async function sendUsageTelemetry(config, cliArgs, payload) {
215
243
  if (!isTelemetryEnabled(config, cliArgs)) {
@@ -256,16 +284,7 @@ export async function sendUsageTelemetry(config, cliArgs, payload) {
256
284
  event: payload?.event || 'app_start',
257
285
  distinct_id: distinctId,
258
286
  timestamp,
259
- properties: {
260
- $process_person_profile: false,
261
- source: 'cli',
262
- app: 'free-coding-models',
263
- version: payload?.version || LOCAL_VERSION,
264
- app_version: payload?.version || LOCAL_VERSION,
265
- mode: payload?.mode || 'opencode',
266
- system: getTelemetrySystem(),
267
- terminal: getTelemetryTerminal(),
268
- },
287
+ properties: buildTelemetryProperties(payload),
269
288
  }
270
289
 
271
290
  await fetch(endpoint, {
@@ -280,6 +280,20 @@ export const TOOL_BOOTSTRAP_METADATA = {
280
280
  },
281
281
  },
282
282
  },
283
+ jcode: {
284
+ binary: 'jcode',
285
+ docsUrl: 'https://github.com/1jehuang/jcode',
286
+ install: {
287
+ default: {
288
+ shellCommand: 'curl -fsSL https://raw.githubusercontent.com/1jehuang/jcode/master/scripts/install.sh | bash',
289
+ summary: 'Install jcode via the official installer script.',
290
+ },
291
+ win32: {
292
+ shellCommand: 'irm https://raw.githubusercontent.com/1jehuang/jcode/master/scripts/install.ps1 | iex',
293
+ summary: 'Install jcode via the official PowerShell installer.',
294
+ },
295
+ },
296
+ },
283
297
  }
284
298
 
285
299
  export function getToolBootstrapMeta(mode) {
@@ -272,7 +272,7 @@ function writePiConfig(model, apiKey, baseUrl, paths = getDefaultToolPaths()) {
272
272
  const modelsBackupPath = backupIfExists(modelsFilePath)
273
273
  const modelsConfig = readJson(modelsFilePath, { providers: {} })
274
274
  if (!modelsConfig.providers || typeof modelsConfig.providers !== 'object') modelsConfig.providers = {}
275
- modelsConfig.providers.freeCodingModels = {
275
+ modelsConfig.providers[model.providerKey] = {
276
276
  baseUrl,
277
277
  api: 'openai-completions',
278
278
  apiKey,
@@ -284,7 +284,7 @@ function writePiConfig(model, apiKey, baseUrl, paths = getDefaultToolPaths()) {
284
284
  const settingsFilePath = paths.piSettingsPath
285
285
  const settingsBackupPath = backupIfExists(settingsFilePath)
286
286
  const settingsConfig = readJson(settingsFilePath, {})
287
- settingsConfig.defaultProvider = 'freeCodingModels'
287
+ settingsConfig.defaultProvider = model.providerKey
288
288
  settingsConfig.defaultModel = model.modelId
289
289
  writeJson(settingsFilePath, settingsConfig)
290
290
 
@@ -667,7 +667,7 @@ export function prepareExternalToolLaunch(mode, model, config, options = {}) {
667
667
  const result = writePiConfig(model, apiKey, baseUrl, paths)
668
668
  return {
669
669
  command: 'pi',
670
- args: ['--provider', 'freeCodingModels', '--model', model.modelId, '--api-key', apiKey],
670
+ args: ['--provider', model.providerKey, '--model', model.modelId, '--api-key', apiKey],
671
671
  env,
672
672
  apiKey,
673
673
  baseUrl,
@@ -758,6 +758,19 @@ export function prepareExternalToolLaunch(mode, model, config, options = {}) {
758
758
  }
759
759
  }
760
760
 
761
+ if (mode === 'jcode') {
762
+ console.log(chalk.dim(` 📖 jcode will use provider: ${model.providerKey} / model: ${model.modelId}`))
763
+ return {
764
+ command: 'jcode',
765
+ args: ['run', '--provider', model.providerKey, '--model', model.modelId, '--api-key', apiKey],
766
+ env,
767
+ apiKey,
768
+ baseUrl,
769
+ meta,
770
+ configArtifacts: [],
771
+ }
772
+ }
773
+
761
774
  return {
762
775
  blocked: true,
763
776
  exitCode: 1,
@@ -854,6 +867,11 @@ export async function startExternalTool(mode, model, config) {
854
867
  return spawnCommand(resolveLaunchCommand(mode, launchPlan.command), launchPlan.args, launchPlan.env)
855
868
  }
856
869
 
870
+ if (mode === 'jcode') {
871
+ console.log(chalk.dim(` 📖 Launching jcode...`))
872
+ return spawnCommand(resolveLaunchCommand(mode, launchPlan.command), launchPlan.args, launchPlan.env)
873
+ }
874
+
857
875
  console.log(chalk.red(` X Unsupported external tool mode: ${mode}`))
858
876
  return 1
859
877
  }
@@ -41,6 +41,7 @@ export const TOOL_METADATA = {
41
41
  cline: { label: 'Cline', emoji: '🧠', flag: '--cline', color: [100, 220, 180] },
42
42
  rovo: { label: 'Rovo Dev CLI', emoji: '🦘', flag: '--rovo', color: [148, 163, 184], cliOnly: true },
43
43
  gemini: { label: 'Gemini CLI', emoji: '♊', flag: '--gemini', color: [66, 165, 245], cliOnly: true },
44
+ jcode: { label: 'jcode', emoji: '🪼', flag: '--jcode', color: [255, 140, 0] },
44
45
  xcode: { label: 'Xcode Intelligence',emoji: '🛠️', flag: '--xcode', color: [20, 126, 251] },
45
46
  }
46
47
 
@@ -62,16 +63,17 @@ export const COMPAT_COLUMN_SLOTS = [
62
63
  { emoji: '🧠', toolKeys: ['cline'], color: [100, 220, 180] },
63
64
  { emoji: '🦘', toolKeys: ['rovo'], color: [148, 163, 184] },
64
65
  { emoji: '♊', toolKeys: ['gemini'], color: [66, 165, 245] },
66
+ { emoji: '🪼', toolKeys: ['jcode'], color: [255, 140, 0] },
65
67
  { emoji: '🛠️', toolKeys: ['xcode'], color: [20, 126, 251] },
66
68
  ]
67
69
 
68
70
  export const TOOL_MODE_ORDER = [
69
71
  'opencode',
72
+ 'pi',
70
73
  'opencode-desktop',
71
74
  'openclaw',
72
75
  'crush',
73
76
  'goose',
74
- 'pi',
75
77
  'aider',
76
78
  'qwen',
77
79
  'openhands',
@@ -82,6 +84,7 @@ export const TOOL_MODE_ORDER = [
82
84
  'xcode',
83
85
  'rovo',
84
86
  'gemini',
87
+ 'jcode',
85
88
  ]
86
89
 
87
90
  export function getToolMeta(mode) {
package/src/utils.js CHANGED
@@ -392,10 +392,10 @@ export function findBestModel(results) {
392
392
  // --openhands, --amp, --pi, --no-telemetry, --json, --help/-h (case-insensitive)
393
393
  // - Value flag: --tier <letter> (the next non-flag arg is the tier value)
394
394
  //
395
- // 📖 Returns:
395
+ // Returns:
396
396
  // { apiKey, bestMode, fiableMode, openCodeMode, openCodeDesktopMode, openClawMode,
397
397
  // aiderMode, crushMode, gooseMode, qwenMode, openHandsMode, ampMode,
398
- // piMode, noTelemetry, jsonMode, helpMode, tierFilter }
398
+ // piMode, jcodeMode, noTelemetry, jsonMode, helpMode, tierFilter }
399
399
  //
400
400
  // 📖 Note: apiKey may be null here — the main CLI falls back to env vars and saved config.
401
401
  export function parseArgs(argv) {
@@ -460,6 +460,7 @@ export function parseArgs(argv) {
460
460
  const clineMode = flags.includes('--cline')
461
461
  const xcodeMode = flags.includes('--xcode')
462
462
  const geminiMode = flags.includes('--gemini')
463
+ const jcodeMode = flags.includes('--jcode')
463
464
  const noTelemetry = flags.includes('--no-telemetry')
464
465
  const jsonMode = flags.includes('--json')
465
466
  const helpMode = flags.includes('--help') || flags.includes('-h')
@@ -505,6 +506,7 @@ export function parseArgs(argv) {
505
506
  xcodeMode,
506
507
  rovoMode,
507
508
  geminiMode,
509
+ jcodeMode,
508
510
  noTelemetry,
509
511
  jsonMode,
510
512
  helpMode,