@forgehive/forge-cli 0.3.17 → 0.5.1

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.
Files changed (96) hide show
  1. package/dist/runner.js +71 -3
  2. package/dist/tasks/auth/add.d.ts +2 -2
  3. package/dist/tasks/auth/add.js +4 -4
  4. package/dist/tasks/auth/clear.d.ts +3 -3
  5. package/dist/tasks/auth/list.d.ts +3 -3
  6. package/dist/tasks/auth/load.d.ts +1 -1
  7. package/dist/tasks/auth/loadCurrent.d.ts +3 -3
  8. package/dist/tasks/auth/remove.d.ts +2 -2
  9. package/dist/tasks/auth/remove.js +1 -1
  10. package/dist/tasks/auth/switch.d.ts +2 -2
  11. package/dist/tasks/auth/switch.js +1 -1
  12. package/dist/tasks/bundle/create.d.ts +2 -2
  13. package/dist/tasks/bundle/create.js +2 -2
  14. package/dist/tasks/bundle/fingerprint.d.ts +2 -2
  15. package/dist/tasks/bundle/fingerprint.js +2 -2
  16. package/dist/tasks/bundle/load.js +1 -1
  17. package/dist/tasks/bundle/zip.js +4 -4
  18. package/dist/tasks/conf/info.d.ts +5 -5
  19. package/dist/tasks/conf/load.d.ts +1 -1
  20. package/dist/tasks/docs/download.js +4 -2
  21. package/dist/tasks/fixture/download.d.ts +4 -4
  22. package/dist/tasks/fixture/download.js +1 -1
  23. package/dist/tasks/init.js +1 -1
  24. package/dist/tasks/project/create.d.ts +5 -5
  25. package/dist/tasks/project/create.js +21 -25
  26. package/dist/tasks/project/link.d.ts +4 -4
  27. package/dist/tasks/project/link.js +1 -1
  28. package/dist/tasks/project/sync.d.ts +7 -7
  29. package/dist/tasks/project/sync.js +7 -3
  30. package/dist/tasks/project/unlink.d.ts +3 -3
  31. package/dist/tasks/runner/bundle.d.ts +2 -2
  32. package/dist/tasks/runner/bundle.js +2 -2
  33. package/dist/tasks/runner/create.d.ts +2 -2
  34. package/dist/tasks/runner/create.js +1 -1
  35. package/dist/tasks/runner/remove.d.ts +2 -2
  36. package/dist/tasks/runner/remove.js +1 -1
  37. package/dist/tasks/task/createTask.d.ts +4 -4
  38. package/dist/tasks/task/createTask.js +3 -2
  39. package/dist/tasks/task/describe.d.ts +2 -2
  40. package/dist/tasks/task/describe.js +1 -1
  41. package/dist/tasks/task/download.d.ts +4 -4
  42. package/dist/tasks/task/download.js +2 -2
  43. package/dist/tasks/task/fingerprint.d.ts +2 -2
  44. package/dist/tasks/task/fingerprint.js +1 -1
  45. package/dist/tasks/task/invoke.d.ts +4 -4
  46. package/dist/tasks/task/invoke.js +2 -2
  47. package/dist/tasks/task/list.d.ts +3 -3
  48. package/dist/tasks/task/publish.d.ts +4 -4
  49. package/dist/tasks/task/publish.js +1 -1
  50. package/dist/tasks/task/remove.d.ts +2 -2
  51. package/dist/tasks/task/remove.js +1 -1
  52. package/dist/tasks/task/replay.d.ts +14 -7
  53. package/dist/tasks/task/replay.js +33 -23
  54. package/dist/tasks/task/run.d.ts +6 -6
  55. package/dist/tasks/task/run.js +18 -23
  56. package/dist/tasks/types.d.ts +1 -0
  57. package/dist/test/tasks/create.test.js +3 -2
  58. package/dist/utils/taskAnalysis.d.ts +6 -0
  59. package/dist/utils/taskAnalysis.js +82 -41
  60. package/package.json +11 -11
  61. package/pnpm-workspace.yaml +2 -0
  62. package/src/runner.ts +80 -3
  63. package/src/tasks/auth/add.ts +4 -4
  64. package/src/tasks/auth/remove.ts +1 -1
  65. package/src/tasks/auth/switch.ts +1 -1
  66. package/src/tasks/bundle/create.ts +2 -2
  67. package/src/tasks/bundle/fingerprint.ts +2 -2
  68. package/src/tasks/bundle/load.ts +1 -1
  69. package/src/tasks/bundle/zip.ts +4 -4
  70. package/src/tasks/docs/download.ts +5 -2
  71. package/src/tasks/fixture/download.ts +1 -1
  72. package/src/tasks/init.ts +1 -1
  73. package/src/tasks/project/create.ts +21 -27
  74. package/src/tasks/project/link.ts +1 -1
  75. package/src/tasks/project/sync.ts +9 -1
  76. package/src/tasks/runner/bundle.ts +2 -2
  77. package/src/tasks/runner/create.ts +1 -1
  78. package/src/tasks/runner/remove.ts +1 -1
  79. package/src/tasks/task/createTask.ts +3 -2
  80. package/src/tasks/task/describe.ts +1 -1
  81. package/src/tasks/task/download.ts +2 -2
  82. package/src/tasks/task/fingerprint.ts +1 -1
  83. package/src/tasks/task/invoke.ts +2 -2
  84. package/src/tasks/task/publish.ts +1 -1
  85. package/src/tasks/task/remove.ts +1 -1
  86. package/src/tasks/task/replay.ts +38 -24
  87. package/src/tasks/task/run.ts +19 -26
  88. package/src/tasks/types.ts +1 -0
  89. package/src/test/tasks/create.test.ts +3 -2
  90. package/src/utils/taskAnalysis.ts +90 -41
  91. package/logs/bundle:fingerprint.log +0 -1
  92. package/logs/task:fingerprint.log +0 -10
  93. package/logs/task:list.log +0 -1
  94. package/logs/test:guidance.log +0 -1
  95. package/logs/test:uuid.log +0 -1
  96. package/logs/test:uuidCheck.log +0 -1
@@ -17,7 +17,7 @@ import { type ForgeConf } from '../types'
17
17
  const description = 'Describe a task with detailed information about its schema, boundaries and configuration'
18
18
 
19
19
  const schema = new Schema({
20
- descriptorName: Schema.string()
20
+ descriptorName: Schema.string().describe('The task descriptor name to describe (e.g. domain:taskName)')
21
21
  })
22
22
 
23
23
  const boundaries = {
@@ -13,8 +13,8 @@ import { loadCurrent as loadCurrentProfile } from '../auth/loadCurrent'
13
13
  import { Profile, type ForgeConf } from '../types'
14
14
 
15
15
  const schema = new Schema({
16
- descriptorName: Schema.string(),
17
- uuid: Schema.string()
16
+ descriptorName: Schema.string().describe('The task descriptor name (e.g. domain:taskName)'),
17
+ uuid: Schema.string().describe('The UUID of the task version to download')
18
18
  })
19
19
 
20
20
  const boundaries = {
@@ -12,7 +12,7 @@ import { fingerprint as bundleFingerprint } from '../bundle/fingerprint'
12
12
  const description = 'Analyze a specific task and generate detailed fingerprint without bundling'
13
13
 
14
14
  const schema = new Schema({
15
- descriptorName: Schema.string()
15
+ descriptorName: Schema.string().describe('The task descriptor name to fingerprint (e.g. domain:taskName)')
16
16
  })
17
17
 
18
18
  const boundaries = {
@@ -14,8 +14,8 @@ const name = 'task:invoke'
14
14
  const description = 'Invoke a deployed task remotely using the Hive API'
15
15
 
16
16
  const schema = new Schema({
17
- descriptorName: Schema.string(),
18
- json: Schema.string()
17
+ descriptorName: Schema.string().describe('The task descriptor name (e.g. domain:taskName)'),
18
+ json: Schema.string().describe('JSON string of arguments to pass to the task')
19
19
  })
20
20
 
21
21
  const boundaries = {
@@ -20,7 +20,7 @@ import { Profile } from '../types'
20
20
  import { TaskFingerprintOutput } from '../../utils/taskAnalysis'
21
21
 
22
22
  const schema = new Schema({
23
- descriptorName: Schema.string()
23
+ descriptorName: Schema.string().describe('The task descriptor name to publish (e.g. domain:taskName)')
24
24
  })
25
25
 
26
26
  const boundaries = {
@@ -11,7 +11,7 @@ import { load } from '../conf/load'
11
11
  import { type ForgeConf } from '../types'
12
12
 
13
13
  const schema = new Schema({
14
- descriptorName: Schema.string()
14
+ descriptorName: Schema.string().describe('The task descriptor name to remove (e.g. domain:taskName)')
15
15
  })
16
16
 
17
17
  const boundaries = {
@@ -7,8 +7,8 @@ import { Schema } from '@forgehive/schema'
7
7
  import fs from 'fs/promises'
8
8
  import path from 'path'
9
9
  import os from 'os'
10
- import axios from 'axios'
11
10
 
11
+ import { createClientFromForgeConf, type ExecutionRecord } from '@forgehive/hive-sdk'
12
12
  import { create as bundleCreate } from '../bundle/create'
13
13
  import { load as bundleLoad } from '../bundle/load'
14
14
  import { load as loadConf } from '../conf/load'
@@ -30,9 +30,9 @@ interface Fixture {
30
30
  const description = 'Replay a task execution from a specified path'
31
31
 
32
32
  const schema = new Schema({
33
- descriptorName: Schema.string(),
34
- path: Schema.string(),
35
- cache: Schema.string().optional()
33
+ descriptorName: Schema.string().describe('The task descriptor name to replay (e.g. domain:taskName)'),
34
+ path: Schema.string().describe('Path to the execution log fixture to replay'),
35
+ cache: Schema.string().describe('Cache mode for boundaries during replay').optional()
36
36
  })
37
37
 
38
38
  const boundaries = {
@@ -66,32 +66,47 @@ const boundaries = {
66
66
 
67
67
  return true
68
68
  },
69
- sendLogToAPI: async (profile: Profile, projectName: string, taskName: string, logItem: unknown, fixtureUUID: string): Promise<boolean> => {
69
+ sendLogToAPI: async (
70
+ profile: Profile,
71
+ record: ExecutionRecord,
72
+ fixtureUUID: string
73
+ ): Promise<{ success: boolean; logUuid?: string }> => {
70
74
  try {
71
- const logsUrl = `${profile.url}/api/tasks/log-ingest`
72
- const authToken = `${profile.apiKey}:${profile.apiSecret}`
73
-
74
- await axios.post(logsUrl, {
75
- projectName,
76
- taskName,
77
- logItem: JSON.stringify(logItem),
78
- replayFrom: fixtureUUID
79
- }, {
80
- headers: {
81
- Authorization: `Bearer ${authToken}`,
82
- 'Content-Type': 'application/json'
75
+ // Use createClientFromForgeConf to automatically load forge.json with task UUIDs
76
+ const client = createClientFromForgeConf('./forge.json', {
77
+ apiKey: profile.apiKey,
78
+ apiSecret: profile.apiSecret,
79
+ host: profile.url,
80
+ metadata: {
81
+ environment: 'cli',
82
+ replayFrom: fixtureUUID
83
83
  }
84
84
  })
85
85
 
86
86
  console.log('===============================================')
87
- console.log('Log sent to API... ', profile.name, profile.url)
88
- console.log('Replay from fixture UUID:', fixtureUUID)
87
+ console.log('Sending replay log to Hive...')
88
+ const result = await client.sendLog(record)
89
89
 
90
- return true
90
+ if (result === 'success' || (typeof result === 'object' && 'uuid' in result)) {
91
+ console.log('✅ Log sent to API...', profile.name, profile.url)
92
+ console.log('Replay from fixture UUID:', fixtureUUID)
93
+ console.log('===============================================')
94
+
95
+ if (typeof result === 'object' && result && 'uuid' in result) {
96
+ return { success: true, logUuid: result.uuid }
97
+ }
98
+
99
+ return { success: true }
100
+ } else {
101
+ console.error('❌ Failed to send log to Hive')
102
+ console.log('===============================================')
103
+ return { success: false }
104
+ }
91
105
  } catch (e) {
92
106
  const error = e as Error
93
- console.error('Failed to send log to API:', error.message)
94
- return false
107
+ console.error('Failed to send log to API:', error.message)
108
+ console.log('===============================================')
109
+ return { success: false }
95
110
  }
96
111
  }
97
112
  }
@@ -107,7 +122,6 @@ export const replay = createTask({
107
122
  // Load forge configuration
108
123
  const forge: ForgeConf = await loadConf({})
109
124
  const taskDescriptor = forge.tasks[descriptorName as keyof typeof forge.tasks]
110
- const projectName = forge.project.name
111
125
 
112
126
  if (taskDescriptor === undefined) {
113
127
  throw new Error(`Task ${descriptorName} is not defined in forge.json`)
@@ -203,7 +217,7 @@ export const replay = createTask({
203
217
  // Send the log to API if profile is available
204
218
  if (profile) {
205
219
  try {
206
- await sendLogToAPI(profile, projectName, descriptorName, record, fixture.fixtureUUID)
220
+ await sendLogToAPI(profile, record, fixture.fixtureUUID)
207
221
  } catch (e) {
208
222
  console.error('Failed to send log to API:', e)
209
223
  }
@@ -10,7 +10,7 @@ import os from 'os'
10
10
  import { createTask } from '@forgehive/task'
11
11
  import { Schema } from '@forgehive/schema'
12
12
  import { RecordTape } from '@forgehive/record-tape'
13
- import { HiveLogClient, type ExecutionRecord } from '@forgehive/hive-sdk'
13
+ import { createClientFromForgeConf, type ExecutionRecord } from '@forgehive/hive-sdk'
14
14
 
15
15
  import { create as bundleCreate } from '../bundle/create'
16
16
  import { load as bundleLoad } from '../bundle/load'
@@ -21,8 +21,8 @@ import { type ForgeConf, type Profile } from '../types'
21
21
  // For now, we'll use a simple schema without the record type
22
22
  // TODO: Use Schema.record once it's properly built and available
23
23
  const schema = new Schema({
24
- descriptorName: Schema.string(),
25
- args: Schema.mixedRecord()
24
+ descriptorName: Schema.string().describe('The task descriptor name to run (e.g. domain:taskName)'),
25
+ args: Schema.mixedRecord().describe('Arguments to pass to the task')
26
26
  // args will be passed directly without schema validation for now
27
27
  })
28
28
 
@@ -51,46 +51,37 @@ const boundaries = {
51
51
  },
52
52
  sendLogToAPI: async (
53
53
  profile: Profile,
54
- projectName: string,
55
54
  record: ExecutionRecord,
56
- taskUuid?: string,
57
- projectUuid?: string
55
+ taskUuid?: string
58
56
  ): Promise<{ success: boolean; logUuid?: string; taskUuid?: string; skipRemoteLog?: boolean }> => {
59
- // Check if we have required UUIDs for the new endpoint
60
- if (!projectUuid || !taskUuid) {
57
+ // Check if we have required UUID for the task
58
+ if (!taskUuid) {
61
59
  console.log('===============================================')
62
- console.log('⚠️ Remote logging skipped - missing UUIDs')
60
+ console.log('⚠️ Remote logging skipped - missing task UUID')
63
61
  console.log('')
64
62
  console.log('To enable remote logging with enhanced features:')
65
- if (!projectUuid) {
66
- console.log('• Use "forge project:create" to create a new project, or')
67
- console.log('• Use "forge project:link" to connect to an existing project')
68
- }
69
- if (!taskUuid) {
70
- console.log('• Use "forge project:sync" to get the task to have UUID')
71
- }
63
+ console.log('• Use "forge project:sync" to sync tasks and get UUIDs')
72
64
  console.log('===============================================')
73
65
  return { success: true, skipRemoteLog: true }
74
66
  }
75
67
 
76
68
  try {
77
- const config = {
78
- projectName,
79
- projectUuid,
69
+ // Use createClientFromForgeConf to automatically load forge.json with task UUIDs
70
+ const client = createClientFromForgeConf('./forge.json', {
80
71
  apiKey: profile.apiKey,
81
72
  apiSecret: profile.apiSecret,
82
73
  host: profile.url,
83
74
  metadata: {
84
75
  environment: 'cli'
85
76
  }
86
- }
77
+ })
87
78
 
88
- const client = new HiveLogClient(config)
79
+ console.log('===============================================')
89
80
  console.log('Sending execution log to Hive...')
90
- const result = await client.sendLogByUuid(record, taskUuid)
81
+ const result = await client.sendLog(record)
91
82
 
92
83
  if (result === 'success' || (typeof result === 'object' && 'uuid' in result)) {
93
- console.log('===============================================')
84
+ console.log('')
94
85
  console.log('✅ Log sent to Hive successfully')
95
86
  console.log(` Profile: ${profile.name}`)
96
87
  console.log(` Host: ${profile.url}`)
@@ -102,13 +93,17 @@ const boundaries = {
102
93
 
103
94
  return { success: true, taskUuid }
104
95
  } else {
96
+ console.log('')
105
97
  console.error('❌ Failed to send log to Hive:', profile.url)
98
+ console.log('===============================================')
106
99
  return { success: false }
107
100
  }
108
101
  } catch (e) {
102
+ console.log('===============================================')
109
103
  console.error('❌ Failed to send log to Hive:', profile.url)
110
104
  const error = e as Error
111
105
  console.error('Error:', error.message)
106
+ console.log('===============================================')
112
107
  return { success: false }
113
108
  }
114
109
  }
@@ -129,8 +124,6 @@ export const run = createTask({
129
124
  // Load forge configuration
130
125
  const forge: ForgeConf = await loadConf({})
131
126
  const taskDescriptor = forge.tasks[descriptorName as keyof typeof forge.tasks]
132
- const projectName = forge.project.name
133
- const projectUuid = forge.project.uuid
134
127
  const taskUuid = taskDescriptor?.uuid
135
128
 
136
129
  if (taskDescriptor === undefined) {
@@ -204,7 +197,7 @@ export const run = createTask({
204
197
 
205
198
  if (profile) {
206
199
  try {
207
- const logResult = await sendLogToAPI(profile, projectName, logItem, taskUuid, projectUuid)
200
+ const logResult = await sendLogToAPI(profile, logItem, taskUuid)
208
201
 
209
202
  if (logResult.success && !logResult.skipRemoteLog && taskUuid) {
210
203
  console.log(`🔗 View execution logs: ${profile.url}/tasks/${taskUuid}?tab=logs`)
@@ -22,6 +22,7 @@ export interface ForgeConf {
22
22
  project: {
23
23
  name: string
24
24
  uuid?: string
25
+ description?: string
25
26
  }
26
27
  paths: {
27
28
  logs: string
@@ -16,8 +16,9 @@ const name = 'sample:newTask'
16
16
  const description = 'Add task description here'
17
17
 
18
18
  const schema = new Schema({
19
- // Add your schema definitions here
20
- // example: myParam: Schema.string()
19
+ // Add your schema definitions here.
20
+ // Use .describe() so the field shows up in \`forge sample:newTask --help\`.
21
+ // example: myParam: Schema.string().describe('What this parameter is for')
21
22
  })
22
23
 
23
24
  const boundaries = {
@@ -9,14 +9,22 @@ interface TaskLocation {
9
9
  interface SchemaProperty {
10
10
  name?: string
11
11
  type: string
12
+ format?: string
13
+ description?: string
14
+ // Internal marker used while building the schema; stripped in favour of the
15
+ // object-level `required` array (JSON Schema semantics) before output.
12
16
  optional?: boolean
13
17
  default?: string
14
18
  properties?: Record<string, SchemaProperty>
19
+ additionalProperties?: SchemaProperty | { anyOf: SchemaProperty[] }
15
20
  }
16
21
 
22
+ // JSON Schema (draft 2020-12) representation of the task input, matching what
23
+ // `Schema.describe()` produces at runtime.
17
24
  interface InputSchema {
18
25
  type: string
19
26
  properties: Record<string, SchemaProperty>
27
+ required?: string[]
20
28
  }
21
29
 
22
30
  interface OutputType {
@@ -558,69 +566,110 @@ function analyzeSchemaArg(node: ts.Expression, sourceFile: ts.SourceFile): Input
558
566
  const arg = node.arguments?.[0]
559
567
  if (arg && ts.isObjectLiteralExpression(arg)) {
560
568
  const properties: Record<string, SchemaProperty> = {}
569
+ const required: string[] = []
561
570
  arg.properties.forEach(prop => {
562
571
  if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
563
572
  const propName = prop.name.text
564
573
  const propValue = analyzeSchemaProp(prop.initializer, sourceFile)
574
+ // Optionality is expressed by absence from `required` (JSON Schema)
575
+ const isOptional = propValue.optional === true
576
+ delete propValue.optional
565
577
  properties[propName] = propValue
578
+ if (!isOptional) {
579
+ required.push(propName)
580
+ }
566
581
  }
567
582
  })
568
- return { type: 'object', properties }
583
+ const result: InputSchema = { type: 'object', properties }
584
+ if (required.length > 0) {
585
+ result.required = required
586
+ }
587
+ return result
569
588
  }
570
589
  }
571
590
  return { type: 'object', properties: {} }
572
591
  }
573
592
 
574
- // Enhanced schema property analysis
575
- function analyzeSchemaProp(node: ts.Expression, sourceFile: ts.SourceFile): SchemaProperty {
576
- // Analyze Schema.string(), Schema.number(), etc.
577
- if (ts.isCallExpression(node)) {
578
- if (ts.isPropertyAccessExpression(node.expression) &&
579
- ts.isIdentifier(node.expression.expression) &&
580
- node.expression.expression.text === 'Schema') {
581
-
582
- const methodName = node.expression.name.text
583
- const baseType: SchemaProperty = { type: getSchemaTypeFromMethod(methodName) }
584
-
585
- return baseType
593
+ // Analyze a single schema field, unwrapping the call chain
594
+ // (e.g. Schema.string().describe('...').optional()) into a JSON Schema property.
595
+ function analyzeSchemaProp(node: ts.Expression, _sourceFile: ts.SourceFile): SchemaProperty {
596
+ let optional = false
597
+ let description: string | undefined
598
+ let defaultValue: string | undefined
599
+
600
+ let current: ts.Expression = node
601
+ while (ts.isCallExpression(current) && ts.isPropertyAccessExpression(current.expression)) {
602
+ const methodName = current.expression.name.text
603
+ const inner = current.expression.expression
604
+
605
+ // Base call: Schema.<type>(...)
606
+ if (ts.isIdentifier(inner) && inner.text === 'Schema') {
607
+ const prop = mapSchemaMethod(methodName)
608
+ if (description !== undefined) {
609
+ prop.description = description
610
+ }
611
+ if (defaultValue !== undefined) {
612
+ prop.default = defaultValue
613
+ }
614
+ if (optional) {
615
+ prop.optional = true
616
+ }
617
+ return prop
586
618
  }
587
- }
588
619
 
589
- // Handle chained calls like Schema.number().optional()
590
- if (ts.isCallExpression(node)) {
591
- if (ts.isPropertyAccessExpression(node.expression)) {
592
- const chainedMethod = node.expression.name.text
593
- if (chainedMethod === 'optional') {
594
- // This is a .optional() call, get the base type
595
- const baseCall = node.expression.expression
596
- if (ts.isCallExpression(baseCall)) {
597
- const baseType = analyzeSchemaProp(baseCall, sourceFile)
598
- return { ...baseType, optional: true }
599
- }
600
- } else if (chainedMethod === 'default') {
601
- // This is a .default() call, get the base type
602
- const baseCall = node.expression.expression
603
- if (ts.isCallExpression(baseCall)) {
604
- const baseType = analyzeSchemaProp(baseCall, sourceFile)
605
- const defaultValue = node.arguments[0]?.getText() || 'undefined'
606
- return { ...baseType, default: defaultValue }
607
- }
620
+ // Chained modifiers
621
+ if (methodName === 'optional') {
622
+ optional = true
623
+ } else if (methodName === 'describe') {
624
+ const arg = current.arguments[0]
625
+ if (arg && ts.isStringLiteral(arg)) {
626
+ description = arg.text
608
627
  }
628
+ } else if (methodName === 'default') {
629
+ defaultValue = current.arguments[0]?.getText() ?? 'undefined'
609
630
  }
631
+ // Other modifiers (min/max/regex/...) don't change the JSON Schema type, so
632
+ // we keep unwrapping until we reach the base Schema.<type>() call.
633
+
634
+ current = inner
610
635
  }
611
636
 
612
637
  return { type: 'unknown' }
613
638
  }
614
639
 
615
- function getSchemaTypeFromMethod(methodName: string): string {
616
- const typeMap: Record<string, string> = {
617
- string: 'string',
618
- number: 'number',
619
- boolean: 'boolean',
620
- array: 'array',
621
- object: 'object'
640
+ // Map a Schema.* helper name to its JSON Schema representation, matching what
641
+ // Schema.describe() emits at runtime.
642
+ function mapSchemaMethod(methodName: string): SchemaProperty {
643
+ switch (methodName) {
644
+ case 'string':
645
+ return { type: 'string' }
646
+ case 'number':
647
+ return { type: 'number' }
648
+ case 'boolean':
649
+ return { type: 'boolean' }
650
+ case 'date':
651
+ return { type: 'string', format: 'date-time' }
652
+ case 'email':
653
+ return { type: 'string', format: 'email' }
654
+ case 'uuid':
655
+ return { type: 'string', format: 'uuid' }
656
+ case 'url':
657
+ return { type: 'string', format: 'uri' }
658
+ case 'array':
659
+ return { type: 'array' }
660
+ case 'object':
661
+ return { type: 'object' }
662
+ case 'stringRecord':
663
+ return { type: 'object', additionalProperties: { type: 'string' } }
664
+ case 'numberRecord':
665
+ return { type: 'object', additionalProperties: { type: 'number' } }
666
+ case 'booleanRecord':
667
+ return { type: 'object', additionalProperties: { type: 'boolean' } }
668
+ case 'mixedRecord':
669
+ return { type: 'object', additionalProperties: { anyOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] } }
670
+ default:
671
+ return { type: 'unknown' }
622
672
  }
623
- return typeMap[methodName] || 'unknown'
624
673
  }
625
674
 
626
675
 
@@ -1 +0,0 @@
1
- {"input":{"filePath":"/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/sample-project/src/tasks/test/errors.ts"},"boundaries":{"getCwd":[],"loadConf":[],"readFile":[],"writeFile":[],"ensureFingerprintsFolder":[]},"metadata":{"environment":"cli"},"metrics":[],"type":"error","error":"Invalid input on: descriptorName: Required"}