@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
@@ -1,10 +0,0 @@
1
- {"name":"task:fingerprint","type":"success","input":{"descriptorName":"task:download"},"output":{"taskName":"download","fingerprint":{"name":"download","location":{"file":"/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/cli/src/tasks/task/download.ts","line":93,"column":25},"inputSchema":{"type":"object","properties":{"descriptorName":{"type":"string"},"uuid":{"type":"string"}}},"outputType":{"type":"object","properties":{"taskPath":{"type":"string"},"fileName":{"type":"string"},"descriptor":{"type":"string"},"handler":{"type":"any"}}},"boundaries":{"loadCurrentProfile":{"inputTypes":["any"],"outputType":"any"},"downloadTask":{"inputTypes":["uuid: string","profile: Profile"],"outputType":"any"},"loadConf":{"inputTypes":["any"],"outputType":"any"},"getCwd":{"inputTypes":[],"outputType":"string"},"parseTaskName":{"inputTypes":["taskDescriptor: string"],"outputType":"Promise<{\n descriptor: string\n taskName: string\n fileName: string\n dir?: string\n }>"},"persistTask":{"inputTypes":["dir: string","fileName: string","content: string","cwd: string"],"outputType":"{ path: string }"},"persistConf":{"inputTypes":["forge: ForgeConf","cwd: string"],"outputType":"void"},"checkTaskExists":{"inputTypes":["dir: string","fileName: string"],"outputType":"boolean"}},"hash":"dbhx5s"},"fingerprintFile":"/Users/danielzavaladlvega/.forge/task:download.task-fingerprint.json","hash":"dbhx5s","analysis":{"inputSchemaProps":["descriptorName","uuid"],"boundaryCount":8,"hasDescription":false,"outputType":"object"}},"boundaries":{"getCwd":[{"input":[],"output":"/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/cli","error":null}],"loadConf":[{"input":[{}],"output":{"project":{"name":"forge-cli"},"paths":{"logs":"logs/","fixtures":"fixtures","tasks":"src/tasks/","runners":"src/runners/","tests":"src/tests/"},"infra":{"region":"us-west-2","bucket":""},"tasks":{"bundle:create":{"path":"src/tasks/bundle/create.ts","handler":"create"},"bundle:load":{"path":"src/tasks/bundle/load.ts","handler":"load"},"task:run":{"path":"src/tasks/task/run.ts","handler":"run"},"task:remove":{"path":"src/tasks/task/remove.ts","handler":"remove"},"conf:info":{"path":"src/tasks/conf/info.ts","handler":"info"},"runner:create":{"path":"src/tasks/runner/create.ts","handler":"create"},"runner:remove":{"path":"src/tasks/runner/remove.ts","handler":"remove"},"runner:bundle":{"path":"src/tasks/runner/bundle.ts","handler":"bundle"},"task:publish":{"path":"src/tasks/task/publish.ts","handler":"publish"},"task:download":{"path":"src/tasks/task/download.ts","handler":"download"},"auth:add":{"path":"src/tasks/auth/add.ts","handler":"add"},"auth:load":{"path":"src/tasks/auth/load.ts","handler":"load"},"auth:loadCurrent":{"path":"src/tasks/auth/loadCurrent.ts","handler":"loadCurrent"},"auth:switch":{"path":"src/tasks/auth/switch.ts","handler":"switchProfile"},"auth:list":{"path":"src/tasks/auth/list.ts","handler":"list"},"auth:remove":{"path":"src/tasks/auth/remove.ts","handler":"remove"},"task:replay":{"path":"src/tasks/task/replay.ts","handler":"replay"},"fixture:download":{"path":"src/tasks/fixture/download.ts","handler":"download"},"bundle:zip":{"path":"src/tasks/bundle/zip.ts","handler":"zip"},"task:list":{"path":"src/tasks/task/list.ts","handler":"list"},"task:describe":{"path":"src/tasks/task/describe.ts","handler":"describe"},"task:fingerprint":{"path":"src/tasks/task/fingerprint.ts","handler":"fingerprint"},"bundle:fingerprint":{"path":"src/tasks/bundle/fingerprint.ts","handler":"fingerprint"}},"runners":{}},"error":null}],"readFile":[{"input":["/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/cli/src/tasks/task/download.ts"],"output":"// TASK: download\n// Run this task with:\n// forge task:run task:download --descriptorName [name] --uuid [task-uuid]\n\nimport { createTask } from '@forgehive/task'\nimport { Schema } from '@forgehive/schema'\nimport axios from 'axios'\nimport path from 'path'\nimport fs from 'fs/promises'\nimport { camelCase } from '../../utils/camelCase'\nimport { load as loadConf } from '../conf/load'\nimport { loadCurrent as loadCurrentProfile } from '../auth/loadCurrent'\nimport { Profile, type ForgeConf } from '../types'\n\nconst schema = new Schema({\n descriptorName: Schema.string(),\n uuid: Schema.string()\n})\n\nconst boundaries = {\n loadCurrentProfile: loadCurrentProfile.asBoundary(),\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n downloadTask: async (uuid: string, profile: Profile): Promise<any> => {\n const downloadUrl = `${profile.url}/api/tasks/download`\n\n console.log(`Downloading task from ${downloadUrl}...`)\n\n const authToken = `${profile.apiKey}:${profile.apiSecret}`\n const response = await axios.post(downloadUrl, { uuid }, {\n headers: {\n Authorization: `Bearer ${authToken}`,\n 'Content-Type': 'application/json'\n }\n })\n\n return response.data\n },\n loadConf: loadConf.asBoundary(),\n getCwd: async (): Promise<string> => {\n return process.cwd()\n },\n parseTaskName: async (taskDescriptor: string): Promise<{\n descriptor: string\n taskName: string\n fileName: string\n dir?: string\n }> => {\n const res: string[] = taskDescriptor.split(':')\n\n if (res.length === 1) {\n return {\n descriptor: `${camelCase(res[0])}`,\n taskName: `${camelCase(res[0])}`,\n fileName: `${camelCase(res[0])}.ts`\n }\n }\n\n return {\n dir: res[0],\n descriptor: `${res[0]}:${camelCase(res[1])}`,\n taskName: `${camelCase(res[1])}`,\n fileName: `${camelCase(res[1])}.ts`\n }\n },\n persistTask: async (dir: string, fileName: string, content: string, cwd: string): Promise<{ path: string }> => {\n const dirPath = path.resolve(cwd, dir)\n const taskPath = path.resolve(dirPath, fileName)\n\n await fs.mkdir(dirPath, { recursive: true })\n await fs.writeFile(taskPath, content, 'utf-8')\n\n\n return {\n path: taskPath.toString()\n }\n },\n persistConf: async (forge: ForgeConf, cwd: string): Promise<void> => {\n const forgePath = path.join(cwd, 'forge.json')\n await fs.writeFile(forgePath, JSON.stringify(forge, null, 2))\n },\n checkTaskExists: async (dir: string, fileName: string): Promise<boolean> => {\n const taskPath = path.resolve(dir, fileName)\n\n try {\n await fs.access(taskPath)\n return true\n } catch {\n return false\n }\n }\n}\n\nexport const download = createTask(\n schema,\n boundaries,\n async function ({ descriptorName, uuid }, {\n downloadTask,\n getCwd,\n parseTaskName,\n persistTask,\n loadConf,\n persistConf,\n checkTaskExists,\n loadCurrentProfile\n }) {\n console.log(`Attempting to download task with descriptor: ${descriptorName} and uuid: ${uuid}`)\n\n // Parse descriptor name to get task details\n const { taskName, fileName, descriptor, dir } = await parseTaskName(descriptorName)\n const profile = await loadCurrentProfile({})\n const cwd = await getCwd()\n const forge = await loadConf({})\n\n let taskPath: string = forge.paths.tasks\n\n if (dir !== undefined) {\n taskPath = path.join(taskPath, dir)\n }\n\n // Check if task already exists\n const taskExists = await checkTaskExists(taskPath, fileName)\n if (taskExists) {\n console.log(`Task ${descriptor} already exists at ${taskPath}/${fileName}`)\n return {\n error: 'Task already exists',\n taskPath: `${taskPath}/${fileName}`,\n descriptor\n }\n }\n\n // Download from hive api server\n let response\n try {\n response = await downloadTask(uuid, profile)\n } catch (e: unknown) {\n const error = e as { status: number, message: string }\n console.error('Error downloading task:', error.message, error.status)\n\n if (error.status === 404) {\n return {\n error: 'Task not found',\n taskPath: `${taskPath}/${fileName}`,\n descriptor\n }\n }\n\n return {\n error: 'Failed to download task',\n message: error.message,\n taskPath: `${taskPath}/${fileName}`,\n descriptor\n }\n }\n\n console.log(`\n ==================================================\n Starting task download!\n Creating: ${taskName}\n Dir: ${dir ?? ''}\n Into: ${taskPath}\n ==================================================\n `)\n\n console.log('Writing task file:', taskPath, fileName)\n console.log('Handler:', response.handler)\n console.log('Source code:', response.sourceCode)\n\n // Persist task with cwd\n await persistTask(taskPath, fileName, response.sourceCode, cwd)\n\n // Update forge.json with the new task\n if (forge.tasks === undefined) {\n forge.tasks = {}\n }\n\n forge.tasks[descriptor] = {\n path: `${taskPath}/${fileName}`,\n handler: response.handler\n }\n\n console.log('Forge:', forge)\n\n await persistConf(forge, cwd)\n\n return {\n taskPath,\n fileName,\n descriptor,\n handler: response.handler\n }\n }\n)\n","error":null}],"writeFile":[{"input":["/Users/danielzavaladlvega/.forge/task:download.task-fingerprint.json","{\n \"taskFingerprint\": {\n \"name\": \"download\",\n \"location\": {\n \"file\": \"/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/cli/src/tasks/task/download.ts\",\n \"line\": 93,\n \"column\": 25\n },\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"descriptorName\": {\n \"type\": \"string\"\n },\n \"uuid\": {\n \"type\": \"string\"\n }\n }\n },\n \"outputType\": {\n \"type\": \"object\",\n \"properties\": {\n \"taskPath\": {\n \"type\": \"string\"\n },\n \"fileName\": {\n \"type\": \"string\"\n },\n \"descriptor\": {\n \"type\": \"string\"\n },\n \"handler\": {\n \"type\": \"any\"\n }\n }\n },\n \"boundaries\": {\n \"loadCurrentProfile\": {\n \"inputTypes\": [\n \"any\"\n ],\n \"outputType\": \"any\"\n },\n \"downloadTask\": {\n \"inputTypes\": [\n \"uuid: string\",\n \"profile: Profile\"\n ],\n \"outputType\": \"any\"\n },\n \"loadConf\": {\n \"inputTypes\": [\n \"any\"\n ],\n \"outputType\": \"any\"\n },\n \"getCwd\": {\n \"inputTypes\": [],\n \"outputType\": \"string\"\n },\n \"parseTaskName\": {\n \"inputTypes\": [\n \"taskDescriptor: string\"\n ],\n \"outputType\": \"Promise<{\\n descriptor: string\\n taskName: string\\n fileName: string\\n dir?: string\\n }>\"\n },\n \"persistTask\": {\n \"inputTypes\": [\n \"dir: string\",\n \"fileName: string\",\n \"content: string\",\n \"cwd: string\"\n ],\n \"outputType\": \"{ path: string }\"\n },\n \"persistConf\": {\n \"inputTypes\": [\n \"forge: ForgeConf\",\n \"cwd: string\"\n ],\n \"outputType\": \"void\"\n },\n \"checkTaskExists\": {\n \"inputTypes\": [\n \"dir: string\",\n \"fileName: string\"\n ],\n \"outputType\": \"boolean\"\n }\n },\n \"hash\": \"dbhx5s\"\n }\n}"],"output":null,"error":null}],"ensureForgeFolder":[{"input":[],"output":"/Users/danielzavaladlvega/.forge","error":null}]},"context":{"environment":"cli"}}
2
- {"name":"task:fingerprint","type":"success","input":{"descriptorName":"task:download"},"output":{"taskName":"download","fingerprint":{"name":"download","location":{"file":"/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/cli/src/tasks/task/download.ts","line":93,"column":25},"inputSchema":{"type":"object","properties":{"descriptorName":{"type":"string"},"uuid":{"type":"string"}}},"outputType":{"type":"object","properties":{"taskPath":{"type":"string"},"fileName":{"type":"string"},"descriptor":{"type":"string"},"handler":{"type":"any"}}},"boundaries":["loadCurrentProfile","downloadTask","loadConf","getCwd","parseTaskName","persistTask","persistConf","checkTaskExists"],"hash":"8f8oel"},"fingerprintFile":"/Users/danielzavaladlvega/.forge/task:download.task-fingerprint.json","hash":"8f8oel","analysis":{"inputSchemaProps":["descriptorName","uuid"],"boundaryCount":8,"hasDescription":false,"outputType":"object"}},"boundaries":{"getCwd":[{"input":[],"output":"/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/cli","error":null}],"loadConf":[{"input":[{}],"output":{"project":{"name":"forge-cli"},"paths":{"logs":"logs/","fixtures":"fixtures","tasks":"src/tasks/","runners":"src/runners/","tests":"src/tests/"},"infra":{"region":"us-west-2","bucket":""},"tasks":{"bundle:create":{"path":"src/tasks/bundle/create.ts","handler":"create"},"bundle:load":{"path":"src/tasks/bundle/load.ts","handler":"load"},"task:run":{"path":"src/tasks/task/run.ts","handler":"run"},"task:remove":{"path":"src/tasks/task/remove.ts","handler":"remove"},"conf:info":{"path":"src/tasks/conf/info.ts","handler":"info"},"runner:create":{"path":"src/tasks/runner/create.ts","handler":"create"},"runner:remove":{"path":"src/tasks/runner/remove.ts","handler":"remove"},"runner:bundle":{"path":"src/tasks/runner/bundle.ts","handler":"bundle"},"task:publish":{"path":"src/tasks/task/publish.ts","handler":"publish"},"task:download":{"path":"src/tasks/task/download.ts","handler":"download"},"auth:add":{"path":"src/tasks/auth/add.ts","handler":"add"},"auth:load":{"path":"src/tasks/auth/load.ts","handler":"load"},"auth:loadCurrent":{"path":"src/tasks/auth/loadCurrent.ts","handler":"loadCurrent"},"auth:switch":{"path":"src/tasks/auth/switch.ts","handler":"switchProfile"},"auth:list":{"path":"src/tasks/auth/list.ts","handler":"list"},"auth:remove":{"path":"src/tasks/auth/remove.ts","handler":"remove"},"task:replay":{"path":"src/tasks/task/replay.ts","handler":"replay"},"fixture:download":{"path":"src/tasks/fixture/download.ts","handler":"download"},"bundle:zip":{"path":"src/tasks/bundle/zip.ts","handler":"zip"},"task:list":{"path":"src/tasks/task/list.ts","handler":"list"},"task:describe":{"path":"src/tasks/task/describe.ts","handler":"describe"},"task:fingerprint":{"path":"src/tasks/task/fingerprint.ts","handler":"fingerprint"},"bundle:fingerprint":{"path":"src/tasks/bundle/fingerprint.ts","handler":"fingerprint"}},"runners":{}},"error":null}],"readFile":[{"input":["/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/cli/src/tasks/task/download.ts"],"output":"// TASK: download\n// Run this task with:\n// forge task:run task:download --descriptorName [name] --uuid [task-uuid]\n\nimport { createTask } from '@forgehive/task'\nimport { Schema } from '@forgehive/schema'\nimport axios from 'axios'\nimport path from 'path'\nimport fs from 'fs/promises'\nimport { camelCase } from '../../utils/camelCase'\nimport { load as loadConf } from '../conf/load'\nimport { loadCurrent as loadCurrentProfile } from '../auth/loadCurrent'\nimport { Profile, type ForgeConf } from '../types'\n\nconst schema = new Schema({\n descriptorName: Schema.string(),\n uuid: Schema.string()\n})\n\nconst boundaries = {\n loadCurrentProfile: loadCurrentProfile.asBoundary(),\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n downloadTask: async (uuid: string, profile: Profile): Promise<any> => {\n const downloadUrl = `${profile.url}/api/tasks/download`\n\n console.log(`Downloading task from ${downloadUrl}...`)\n\n const authToken = `${profile.apiKey}:${profile.apiSecret}`\n const response = await axios.post(downloadUrl, { uuid }, {\n headers: {\n Authorization: `Bearer ${authToken}`,\n 'Content-Type': 'application/json'\n }\n })\n\n return response.data\n },\n loadConf: loadConf.asBoundary(),\n getCwd: async (): Promise<string> => {\n return process.cwd()\n },\n parseTaskName: async (taskDescriptor: string): Promise<{\n descriptor: string\n taskName: string\n fileName: string\n dir?: string\n }> => {\n const res: string[] = taskDescriptor.split(':')\n\n if (res.length === 1) {\n return {\n descriptor: `${camelCase(res[0])}`,\n taskName: `${camelCase(res[0])}`,\n fileName: `${camelCase(res[0])}.ts`\n }\n }\n\n return {\n dir: res[0],\n descriptor: `${res[0]}:${camelCase(res[1])}`,\n taskName: `${camelCase(res[1])}`,\n fileName: `${camelCase(res[1])}.ts`\n }\n },\n persistTask: async (dir: string, fileName: string, content: string, cwd: string): Promise<{ path: string }> => {\n const dirPath = path.resolve(cwd, dir)\n const taskPath = path.resolve(dirPath, fileName)\n\n await fs.mkdir(dirPath, { recursive: true })\n await fs.writeFile(taskPath, content, 'utf-8')\n\n\n return {\n path: taskPath.toString()\n }\n },\n persistConf: async (forge: ForgeConf, cwd: string): Promise<void> => {\n const forgePath = path.join(cwd, 'forge.json')\n await fs.writeFile(forgePath, JSON.stringify(forge, null, 2))\n },\n checkTaskExists: async (dir: string, fileName: string): Promise<boolean> => {\n const taskPath = path.resolve(dir, fileName)\n\n try {\n await fs.access(taskPath)\n return true\n } catch {\n return false\n }\n }\n}\n\nexport const download = createTask(\n schema,\n boundaries,\n async function ({ descriptorName, uuid }, {\n downloadTask,\n getCwd,\n parseTaskName,\n persistTask,\n loadConf,\n persistConf,\n checkTaskExists,\n loadCurrentProfile\n }) {\n console.log(`Attempting to download task with descriptor: ${descriptorName} and uuid: ${uuid}`)\n\n // Parse descriptor name to get task details\n const { taskName, fileName, descriptor, dir } = await parseTaskName(descriptorName)\n const profile = await loadCurrentProfile({})\n const cwd = await getCwd()\n const forge = await loadConf({})\n\n let taskPath: string = forge.paths.tasks\n\n if (dir !== undefined) {\n taskPath = path.join(taskPath, dir)\n }\n\n // Check if task already exists\n const taskExists = await checkTaskExists(taskPath, fileName)\n if (taskExists) {\n console.log(`Task ${descriptor} already exists at ${taskPath}/${fileName}`)\n return {\n error: 'Task already exists',\n taskPath: `${taskPath}/${fileName}`,\n descriptor\n }\n }\n\n // Download from hive api server\n let response\n try {\n response = await downloadTask(uuid, profile)\n } catch (e: unknown) {\n const error = e as { status: number, message: string }\n console.error('Error downloading task:', error.message, error.status)\n\n if (error.status === 404) {\n return {\n error: 'Task not found',\n taskPath: `${taskPath}/${fileName}`,\n descriptor\n }\n }\n\n return {\n error: 'Failed to download task',\n message: error.message,\n taskPath: `${taskPath}/${fileName}`,\n descriptor\n }\n }\n\n console.log(`\n ==================================================\n Starting task download!\n Creating: ${taskName}\n Dir: ${dir ?? ''}\n Into: ${taskPath}\n ==================================================\n `)\n\n console.log('Writing task file:', taskPath, fileName)\n console.log('Handler:', response.handler)\n console.log('Source code:', response.sourceCode)\n\n // Persist task with cwd\n await persistTask(taskPath, fileName, response.sourceCode, cwd)\n\n // Update forge.json with the new task\n if (forge.tasks === undefined) {\n forge.tasks = {}\n }\n\n forge.tasks[descriptor] = {\n path: `${taskPath}/${fileName}`,\n handler: response.handler\n }\n\n console.log('Forge:', forge)\n\n await persistConf(forge, cwd)\n\n return {\n taskPath,\n fileName,\n descriptor,\n handler: response.handler\n }\n }\n)\n","error":null}],"writeFile":[{"input":["/Users/danielzavaladlvega/.forge/task:download.task-fingerprint.json","{\n \"taskFingerprint\": {\n \"name\": \"download\",\n \"location\": {\n \"file\": \"/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/cli/src/tasks/task/download.ts\",\n \"line\": 93,\n \"column\": 25\n },\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"descriptorName\": {\n \"type\": \"string\"\n },\n \"uuid\": {\n \"type\": \"string\"\n }\n }\n },\n \"outputType\": {\n \"type\": \"object\",\n \"properties\": {\n \"taskPath\": {\n \"type\": \"string\"\n },\n \"fileName\": {\n \"type\": \"string\"\n },\n \"descriptor\": {\n \"type\": \"string\"\n },\n \"handler\": {\n \"type\": \"any\"\n }\n }\n },\n \"boundaries\": [\n \"loadCurrentProfile\",\n \"downloadTask\",\n \"loadConf\",\n \"getCwd\",\n \"parseTaskName\",\n \"persistTask\",\n \"persistConf\",\n \"checkTaskExists\"\n ],\n \"hash\": \"8f8oel\"\n }\n}"],"output":null,"error":null}],"ensureForgeFolder":[{"input":[],"output":"/Users/danielzavaladlvega/.forge","error":null}]},"context":{"environment":"cli"}}
3
- {"name":"task:fingerprint","type":"success","input":{"descriptorName":"task:download"},"output":{"taskName":"download","fingerprint":{"inputSchema":{"type":"object","properties":{"descriptorName":{"type":"string"},"uuid":{"type":"string"}}},"outputType":{"type":"object","properties":{"taskPath":{"type":"string"},"fileName":{"type":"string"},"descriptor":{"type":"string"},"handler":{"type":"any"}}},"boundaries":["loadCurrentProfile","downloadTask","loadConf","getCwd","parseTaskName","persistTask","persistConf","checkTaskExists"]},"fingerprintFile":"/Users/danielzavaladlvega/.forge/task:download.task-fingerprint.json","hash":"8f8oel","analysis":{"inputSchemaProps":["descriptorName","uuid"],"boundaryCount":8,"hasDescription":false,"outputType":"object"}},"boundaries":{"getCwd":[{"input":[],"output":"/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/cli","error":null}],"loadConf":[{"input":[{}],"output":{"project":{"name":"forge-cli"},"paths":{"logs":"logs/","fixtures":"fixtures","tasks":"src/tasks/","runners":"src/runners/","tests":"src/tests/"},"infra":{"region":"us-west-2","bucket":""},"tasks":{"bundle:create":{"path":"src/tasks/bundle/create.ts","handler":"create"},"bundle:load":{"path":"src/tasks/bundle/load.ts","handler":"load"},"task:run":{"path":"src/tasks/task/run.ts","handler":"run"},"task:remove":{"path":"src/tasks/task/remove.ts","handler":"remove"},"conf:info":{"path":"src/tasks/conf/info.ts","handler":"info"},"runner:create":{"path":"src/tasks/runner/create.ts","handler":"create"},"runner:remove":{"path":"src/tasks/runner/remove.ts","handler":"remove"},"runner:bundle":{"path":"src/tasks/runner/bundle.ts","handler":"bundle"},"task:publish":{"path":"src/tasks/task/publish.ts","handler":"publish"},"task:download":{"path":"src/tasks/task/download.ts","handler":"download"},"auth:add":{"path":"src/tasks/auth/add.ts","handler":"add"},"auth:load":{"path":"src/tasks/auth/load.ts","handler":"load"},"auth:loadCurrent":{"path":"src/tasks/auth/loadCurrent.ts","handler":"loadCurrent"},"auth:switch":{"path":"src/tasks/auth/switch.ts","handler":"switchProfile"},"auth:list":{"path":"src/tasks/auth/list.ts","handler":"list"},"auth:remove":{"path":"src/tasks/auth/remove.ts","handler":"remove"},"task:replay":{"path":"src/tasks/task/replay.ts","handler":"replay"},"fixture:download":{"path":"src/tasks/fixture/download.ts","handler":"download"},"bundle:zip":{"path":"src/tasks/bundle/zip.ts","handler":"zip"},"task:list":{"path":"src/tasks/task/list.ts","handler":"list"},"task:describe":{"path":"src/tasks/task/describe.ts","handler":"describe"},"task:fingerprint":{"path":"src/tasks/task/fingerprint.ts","handler":"fingerprint"},"bundle:fingerprint":{"path":"src/tasks/bundle/fingerprint.ts","handler":"fingerprint"}},"runners":{}},"error":null}],"readFile":[{"input":["/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/cli/src/tasks/task/download.ts"],"output":"// TASK: download\n// Run this task with:\n// forge task:run task:download --descriptorName [name] --uuid [task-uuid]\n\nimport { createTask } from '@forgehive/task'\nimport { Schema } from '@forgehive/schema'\nimport axios from 'axios'\nimport path from 'path'\nimport fs from 'fs/promises'\nimport { camelCase } from '../../utils/camelCase'\nimport { load as loadConf } from '../conf/load'\nimport { loadCurrent as loadCurrentProfile } from '../auth/loadCurrent'\nimport { Profile, type ForgeConf } from '../types'\n\nconst schema = new Schema({\n descriptorName: Schema.string(),\n uuid: Schema.string()\n})\n\nconst boundaries = {\n loadCurrentProfile: loadCurrentProfile.asBoundary(),\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n downloadTask: async (uuid: string, profile: Profile): Promise<any> => {\n const downloadUrl = `${profile.url}/api/tasks/download`\n\n console.log(`Downloading task from ${downloadUrl}...`)\n\n const authToken = `${profile.apiKey}:${profile.apiSecret}`\n const response = await axios.post(downloadUrl, { uuid }, {\n headers: {\n Authorization: `Bearer ${authToken}`,\n 'Content-Type': 'application/json'\n }\n })\n\n return response.data\n },\n loadConf: loadConf.asBoundary(),\n getCwd: async (): Promise<string> => {\n return process.cwd()\n },\n parseTaskName: async (taskDescriptor: string): Promise<{\n descriptor: string\n taskName: string\n fileName: string\n dir?: string\n }> => {\n const res: string[] = taskDescriptor.split(':')\n\n if (res.length === 1) {\n return {\n descriptor: `${camelCase(res[0])}`,\n taskName: `${camelCase(res[0])}`,\n fileName: `${camelCase(res[0])}.ts`\n }\n }\n\n return {\n dir: res[0],\n descriptor: `${res[0]}:${camelCase(res[1])}`,\n taskName: `${camelCase(res[1])}`,\n fileName: `${camelCase(res[1])}.ts`\n }\n },\n persistTask: async (dir: string, fileName: string, content: string, cwd: string): Promise<{ path: string }> => {\n const dirPath = path.resolve(cwd, dir)\n const taskPath = path.resolve(dirPath, fileName)\n\n await fs.mkdir(dirPath, { recursive: true })\n await fs.writeFile(taskPath, content, 'utf-8')\n\n\n return {\n path: taskPath.toString()\n }\n },\n persistConf: async (forge: ForgeConf, cwd: string): Promise<void> => {\n const forgePath = path.join(cwd, 'forge.json')\n await fs.writeFile(forgePath, JSON.stringify(forge, null, 2))\n },\n checkTaskExists: async (dir: string, fileName: string): Promise<boolean> => {\n const taskPath = path.resolve(dir, fileName)\n\n try {\n await fs.access(taskPath)\n return true\n } catch {\n return false\n }\n }\n}\n\nexport const download = createTask(\n schema,\n boundaries,\n async function ({ descriptorName, uuid }, {\n downloadTask,\n getCwd,\n parseTaskName,\n persistTask,\n loadConf,\n persistConf,\n checkTaskExists,\n loadCurrentProfile\n }) {\n console.log(`Attempting to download task with descriptor: ${descriptorName} and uuid: ${uuid}`)\n\n // Parse descriptor name to get task details\n const { taskName, fileName, descriptor, dir } = await parseTaskName(descriptorName)\n const profile = await loadCurrentProfile({})\n const cwd = await getCwd()\n const forge = await loadConf({})\n\n let taskPath: string = forge.paths.tasks\n\n if (dir !== undefined) {\n taskPath = path.join(taskPath, dir)\n }\n\n // Check if task already exists\n const taskExists = await checkTaskExists(taskPath, fileName)\n if (taskExists) {\n console.log(`Task ${descriptor} already exists at ${taskPath}/${fileName}`)\n return {\n error: 'Task already exists',\n taskPath: `${taskPath}/${fileName}`,\n descriptor\n }\n }\n\n // Download from hive api server\n let response\n try {\n response = await downloadTask(uuid, profile)\n } catch (e: unknown) {\n const error = e as { status: number, message: string }\n console.error('Error downloading task:', error.message, error.status)\n\n if (error.status === 404) {\n return {\n error: 'Task not found',\n taskPath: `${taskPath}/${fileName}`,\n descriptor\n }\n }\n\n return {\n error: 'Failed to download task',\n message: error.message,\n taskPath: `${taskPath}/${fileName}`,\n descriptor\n }\n }\n\n console.log(`\n ==================================================\n Starting task download!\n Creating: ${taskName}\n Dir: ${dir ?? ''}\n Into: ${taskPath}\n ==================================================\n `)\n\n console.log('Writing task file:', taskPath, fileName)\n console.log('Handler:', response.handler)\n console.log('Source code:', response.sourceCode)\n\n // Persist task with cwd\n await persistTask(taskPath, fileName, response.sourceCode, cwd)\n\n // Update forge.json with the new task\n if (forge.tasks === undefined) {\n forge.tasks = {}\n }\n\n forge.tasks[descriptor] = {\n path: `${taskPath}/${fileName}`,\n handler: response.handler\n }\n\n console.log('Forge:', forge)\n\n await persistConf(forge, cwd)\n\n return {\n taskPath,\n fileName,\n descriptor,\n handler: response.handler\n }\n }\n)\n","error":null}],"writeFile":[{"input":["/Users/danielzavaladlvega/.forge/task:download.task-fingerprint.json","{\n \"taskFingerprint\": {\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"descriptorName\": {\n \"type\": \"string\"\n },\n \"uuid\": {\n \"type\": \"string\"\n }\n }\n },\n \"outputType\": {\n \"type\": \"object\",\n \"properties\": {\n \"taskPath\": {\n \"type\": \"string\"\n },\n \"fileName\": {\n \"type\": \"string\"\n },\n \"descriptor\": {\n \"type\": \"string\"\n },\n \"handler\": {\n \"type\": \"any\"\n }\n }\n },\n \"boundaries\": [\n \"loadCurrentProfile\",\n \"downloadTask\",\n \"loadConf\",\n \"getCwd\",\n \"parseTaskName\",\n \"persistTask\",\n \"persistConf\",\n \"checkTaskExists\"\n ]\n }\n}"],"output":null,"error":null}],"ensureForgeFolder":[{"input":[],"output":"/Users/danielzavaladlvega/.forge","error":null}]},"context":{"environment":"cli"}}
4
- {"name":"task:fingerprint","type":"error","input":{"descriptorName":"task:download"},"error":"Invalid input on: path: Required","boundaries":{"getCwd":[],"readFile":[],"writeFile":[],"ensureForgeFolder":[]},"context":{"environment":"cli"}}
5
- {"name":"task:fingerprint","type":"error","input":{"descriptorName":"task:download"},"error":"Invalid input on: path: Required","boundaries":{"getCwd":[],"readFile":[],"writeFile":[],"ensureForgeFolder":[]},"context":{"environment":"cli"}}
6
- {"name":"task:fingerprint","type":"success","input":{"descriptorName":"task:download"},"output":{"taskName":"task:download","fingerprint":{"inputSchema":{"type":"object","properties":{"descriptorName":{"type":"string"},"uuid":{"type":"string"}}},"outputType":{"type":"object","properties":{"taskPath":{"type":"string"},"fileName":{"type":"string"},"descriptor":{"type":"string"},"handler":{"type":"any"}}},"boundaries":["loadCurrentProfile","downloadTask","loadConf","getCwd","parseTaskName","persistTask","persistConf","checkTaskExists"]},"fingerprintFile":"/Users/danielzavaladlvega/.forge/task:download.task-fingerprint.json","analysis":{"inputSchemaProps":["descriptorName","uuid"],"boundaryCount":8,"hasDescription":false,"outputType":"object"}},"boundaries":{"getCwd":[{"input":[],"output":"/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/cli","error":null}],"loadConf":[{"input":[{}],"output":{"project":{"name":"forge-cli"},"paths":{"logs":"logs/","fixtures":"fixtures","tasks":"src/tasks/","runners":"src/runners/","tests":"src/tests/"},"infra":{"region":"us-west-2","bucket":""},"tasks":{"bundle:create":{"path":"src/tasks/bundle/create.ts","handler":"create"},"bundle:load":{"path":"src/tasks/bundle/load.ts","handler":"load"},"task:run":{"path":"src/tasks/task/run.ts","handler":"run"},"task:remove":{"path":"src/tasks/task/remove.ts","handler":"remove"},"conf:info":{"path":"src/tasks/conf/info.ts","handler":"info"},"runner:create":{"path":"src/tasks/runner/create.ts","handler":"create"},"runner:remove":{"path":"src/tasks/runner/remove.ts","handler":"remove"},"runner:bundle":{"path":"src/tasks/runner/bundle.ts","handler":"bundle"},"task:publish":{"path":"src/tasks/task/publish.ts","handler":"publish"},"task:download":{"path":"src/tasks/task/download.ts","handler":"download"},"auth:add":{"path":"src/tasks/auth/add.ts","handler":"add"},"auth:load":{"path":"src/tasks/auth/load.ts","handler":"load"},"auth:loadCurrent":{"path":"src/tasks/auth/loadCurrent.ts","handler":"loadCurrent"},"auth:switch":{"path":"src/tasks/auth/switch.ts","handler":"switchProfile"},"auth:list":{"path":"src/tasks/auth/list.ts","handler":"list"},"auth:remove":{"path":"src/tasks/auth/remove.ts","handler":"remove"},"task:replay":{"path":"src/tasks/task/replay.ts","handler":"replay"},"fixture:download":{"path":"src/tasks/fixture/download.ts","handler":"download"},"bundle:zip":{"path":"src/tasks/bundle/zip.ts","handler":"zip"},"task:list":{"path":"src/tasks/task/list.ts","handler":"list"},"task:describe":{"path":"src/tasks/task/describe.ts","handler":"describe"},"task:fingerprint":{"path":"src/tasks/task/fingerprint.ts","handler":"fingerprint"},"bundle:fingerprint":{"path":"src/tasks/bundle/fingerprint.ts","handler":"fingerprint"}},"runners":{}},"error":null}],"readFile":[{"input":["/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/cli/src/tasks/task/download.ts"],"output":"// TASK: download\n// Run this task with:\n// forge task:run task:download --descriptorName [name] --uuid [task-uuid]\n\nimport { createTask } from '@forgehive/task'\nimport { Schema } from '@forgehive/schema'\nimport axios from 'axios'\nimport path from 'path'\nimport fs from 'fs/promises'\nimport { camelCase } from '../../utils/camelCase'\nimport { load as loadConf } from '../conf/load'\nimport { loadCurrent as loadCurrentProfile } from '../auth/loadCurrent'\nimport { Profile, type ForgeConf } from '../types'\n\nconst schema = new Schema({\n descriptorName: Schema.string(),\n uuid: Schema.string()\n})\n\nconst boundaries = {\n loadCurrentProfile: loadCurrentProfile.asBoundary(),\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n downloadTask: async (uuid: string, profile: Profile): Promise<any> => {\n const downloadUrl = `${profile.url}/api/tasks/download`\n\n console.log(`Downloading task from ${downloadUrl}...`)\n\n const authToken = `${profile.apiKey}:${profile.apiSecret}`\n const response = await axios.post(downloadUrl, { uuid }, {\n headers: {\n Authorization: `Bearer ${authToken}`,\n 'Content-Type': 'application/json'\n }\n })\n\n return response.data\n },\n loadConf: loadConf.asBoundary(),\n getCwd: async (): Promise<string> => {\n return process.cwd()\n },\n parseTaskName: async (taskDescriptor: string): Promise<{\n descriptor: string\n taskName: string\n fileName: string\n dir?: string\n }> => {\n const res: string[] = taskDescriptor.split(':')\n\n if (res.length === 1) {\n return {\n descriptor: `${camelCase(res[0])}`,\n taskName: `${camelCase(res[0])}`,\n fileName: `${camelCase(res[0])}.ts`\n }\n }\n\n return {\n dir: res[0],\n descriptor: `${res[0]}:${camelCase(res[1])}`,\n taskName: `${camelCase(res[1])}`,\n fileName: `${camelCase(res[1])}.ts`\n }\n },\n persistTask: async (dir: string, fileName: string, content: string, cwd: string): Promise<{ path: string }> => {\n const dirPath = path.resolve(cwd, dir)\n const taskPath = path.resolve(dirPath, fileName)\n\n await fs.mkdir(dirPath, { recursive: true })\n await fs.writeFile(taskPath, content, 'utf-8')\n\n\n return {\n path: taskPath.toString()\n }\n },\n persistConf: async (forge: ForgeConf, cwd: string): Promise<void> => {\n const forgePath = path.join(cwd, 'forge.json')\n await fs.writeFile(forgePath, JSON.stringify(forge, null, 2))\n },\n checkTaskExists: async (dir: string, fileName: string): Promise<boolean> => {\n const taskPath = path.resolve(dir, fileName)\n\n try {\n await fs.access(taskPath)\n return true\n } catch {\n return false\n }\n }\n}\n\nexport const download = createTask(\n schema,\n boundaries,\n async function ({ descriptorName, uuid }, {\n downloadTask,\n getCwd,\n parseTaskName,\n persistTask,\n loadConf,\n persistConf,\n checkTaskExists,\n loadCurrentProfile\n }) {\n console.log(`Attempting to download task with descriptor: ${descriptorName} and uuid: ${uuid}`)\n\n // Parse descriptor name to get task details\n const { taskName, fileName, descriptor, dir } = await parseTaskName(descriptorName)\n const profile = await loadCurrentProfile({})\n const cwd = await getCwd()\n const forge = await loadConf({})\n\n let taskPath: string = forge.paths.tasks\n\n if (dir !== undefined) {\n taskPath = path.join(taskPath, dir)\n }\n\n // Check if task already exists\n const taskExists = await checkTaskExists(taskPath, fileName)\n if (taskExists) {\n console.log(`Task ${descriptor} already exists at ${taskPath}/${fileName}`)\n return {\n error: 'Task already exists',\n taskPath: `${taskPath}/${fileName}`,\n descriptor\n }\n }\n\n // Download from hive api server\n let response\n try {\n response = await downloadTask(uuid, profile)\n } catch (e: unknown) {\n const error = e as { status: number, message: string }\n console.error('Error downloading task:', error.message, error.status)\n\n if (error.status === 404) {\n return {\n error: 'Task not found',\n taskPath: `${taskPath}/${fileName}`,\n descriptor\n }\n }\n\n return {\n error: 'Failed to download task',\n message: error.message,\n taskPath: `${taskPath}/${fileName}`,\n descriptor\n }\n }\n\n console.log(`\n ==================================================\n Starting task download!\n Creating: ${taskName}\n Dir: ${dir ?? ''}\n Into: ${taskPath}\n ==================================================\n `)\n\n console.log('Writing task file:', taskPath, fileName)\n console.log('Handler:', response.handler)\n console.log('Source code:', response.sourceCode)\n\n // Persist task with cwd\n await persistTask(taskPath, fileName, response.sourceCode, cwd)\n\n // Update forge.json with the new task\n if (forge.tasks === undefined) {\n forge.tasks = {}\n }\n\n forge.tasks[descriptor] = {\n path: `${taskPath}/${fileName}`,\n handler: response.handler\n }\n\n console.log('Forge:', forge)\n\n await persistConf(forge, cwd)\n\n return {\n taskPath,\n fileName,\n descriptor,\n handler: response.handler\n }\n }\n)\n","error":null}],"writeFile":[{"input":["/Users/danielzavaladlvega/.forge/task:download.task-fingerprint.json","{\n \"taskFingerprint\": {\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"descriptorName\": {\n \"type\": \"string\"\n },\n \"uuid\": {\n \"type\": \"string\"\n }\n }\n },\n \"outputType\": {\n \"type\": \"object\",\n \"properties\": {\n \"taskPath\": {\n \"type\": \"string\"\n },\n \"fileName\": {\n \"type\": \"string\"\n },\n \"descriptor\": {\n \"type\": \"string\"\n },\n \"handler\": {\n \"type\": \"any\"\n }\n }\n },\n \"boundaries\": [\n \"loadCurrentProfile\",\n \"downloadTask\",\n \"loadConf\",\n \"getCwd\",\n \"parseTaskName\",\n \"persistTask\",\n \"persistConf\",\n \"checkTaskExists\"\n ]\n }\n}"],"output":null,"error":null}],"ensureForgeFolder":[{"input":[],"output":"/Users/danielzavaladlvega/.forge","error":null}]},"context":{"environment":"cli"}}
7
- {"name":"task:fingerprint","type":"success","input":{"descriptorName":"task:run"},"output":{"taskName":"task:run","fingerprint":{"inputSchema":{"type":"object","properties":{"descriptorName":{"type":"string"},"args":{"type":"unknown"}}},"outputType":{"type":"any"},"boundaries":["loadConf","loadCurrentProfile","bundleCreate","bundleLoad","verifyLogFolder","ensureBuildsFolder","sendLogToAPI"]},"fingerprintFile":"/Users/danielzavaladlvega/.forge/task:run.task-fingerprint.json","analysis":{"inputSchemaProps":["descriptorName","args"],"boundaryCount":7,"hasDescription":false,"outputType":"any"}},"boundaries":{"getCwd":[{"input":[],"output":"/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/cli","error":null}],"loadConf":[{"input":[{}],"output":{"project":{"name":"forge-cli"},"paths":{"logs":"logs/","fixtures":"fixtures","tasks":"src/tasks/","runners":"src/runners/","tests":"src/tests/"},"infra":{"region":"us-west-2","bucket":""},"tasks":{"bundle:create":{"path":"src/tasks/bundle/create.ts","handler":"create"},"bundle:load":{"path":"src/tasks/bundle/load.ts","handler":"load"},"task:run":{"path":"src/tasks/task/run.ts","handler":"run"},"task:remove":{"path":"src/tasks/task/remove.ts","handler":"remove"},"conf:info":{"path":"src/tasks/conf/info.ts","handler":"info"},"runner:create":{"path":"src/tasks/runner/create.ts","handler":"create"},"runner:remove":{"path":"src/tasks/runner/remove.ts","handler":"remove"},"runner:bundle":{"path":"src/tasks/runner/bundle.ts","handler":"bundle"},"task:publish":{"path":"src/tasks/task/publish.ts","handler":"publish"},"task:download":{"path":"src/tasks/task/download.ts","handler":"download"},"auth:add":{"path":"src/tasks/auth/add.ts","handler":"add"},"auth:load":{"path":"src/tasks/auth/load.ts","handler":"load"},"auth:loadCurrent":{"path":"src/tasks/auth/loadCurrent.ts","handler":"loadCurrent"},"auth:switch":{"path":"src/tasks/auth/switch.ts","handler":"switchProfile"},"auth:list":{"path":"src/tasks/auth/list.ts","handler":"list"},"auth:remove":{"path":"src/tasks/auth/remove.ts","handler":"remove"},"task:replay":{"path":"src/tasks/task/replay.ts","handler":"replay"},"fixture:download":{"path":"src/tasks/fixture/download.ts","handler":"download"},"bundle:zip":{"path":"src/tasks/bundle/zip.ts","handler":"zip"},"task:list":{"path":"src/tasks/task/list.ts","handler":"list"},"task:describe":{"path":"src/tasks/task/describe.ts","handler":"describe"},"task:fingerprint":{"path":"src/tasks/task/fingerprint.ts","handler":"fingerprint"},"bundle:fingerprint":{"path":"src/tasks/bundle/fingerprint.ts","handler":"fingerprint"}},"runners":{}},"error":null}],"readFile":[{"input":["/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/cli/src/tasks/task/run.ts"],"output":"// TASK: run\n// Run this task with:\n// shadow-cli task:run\n\nimport path from 'path'\nimport fs from 'fs/promises'\nimport os from 'os'\nimport axios from 'axios'\n\nimport { createTask } from '@forgehive/task'\nimport { Schema } from '@forgehive/schema'\nimport { RecordTape } from '@forgehive/record-tape'\n\nimport { create as bundleCreate } from '../bundle/create'\nimport { load as bundleLoad } from '../bundle/load'\nimport { load as loadConf } from '../conf/load'\nimport { loadCurrent as loadCurrentProfile } from '../auth/loadCurrent'\nimport { type ForgeConf, type Profile } from '../types'\n\n// For now, we'll use a simple schema without the record type\n// TODO: Use Schema.record once it's properly built and available\nconst schema = new Schema({\n descriptorName: Schema.string(),\n args: Schema.mixedRecord()\n // args will be passed directly without schema validation for now\n})\n\nconst boundaries = {\n loadConf: loadConf.asBoundary(),\n loadCurrentProfile: loadCurrentProfile.asBoundary(),\n bundleCreate: bundleCreate.asBoundary(),\n bundleLoad: bundleLoad.asBoundary(),\n verifyLogFolder: async (logsPath: string): Promise<boolean> => {\n // return true if the folder exists\n try {\n await fs.access(logsPath)\n } catch (error) {\n return false\n }\n\n return true\n },\n ensureBuildsFolder: async (): Promise<string> => {\n const buildsPath = path.join(os.homedir(), '.forge', 'builds')\n try {\n await fs.access(buildsPath)\n } catch {\n await fs.mkdir(buildsPath, { recursive: true })\n }\n\n return buildsPath\n },\n sendLogToAPI: async (profile: Profile, projectName: string, taskName: string, logItem: unknown): Promise<boolean> => {\n try {\n const logsUrl = `${profile.url}/api/tasks/log-ingest`\n const authToken = `${profile.apiKey}:${profile.apiSecret}`\n\n await axios.post(logsUrl, {\n projectName,\n taskName,\n logItem: JSON.stringify(logItem)\n }, {\n headers: {\n Authorization: `Bearer ${authToken}`,\n 'Content-Type': 'application/json'\n }\n })\n\n console.log('===============================================')\n console.log('Log sent to API... ', profile.name, profile.url)\n\n return true\n } catch (e) {\n const error = e as Error\n console.error('Failed to send log to API:', error.message)\n return false\n }\n }\n}\n\nexport const run = createTask(\n schema,\n boundaries,\n async function ({ descriptorName, args }, {\n loadConf,\n bundleCreate,\n bundleLoad,\n verifyLogFolder,\n ensureBuildsFolder,\n loadCurrentProfile,\n sendLogToAPI\n }) {\n // Load forge configuration\n const forge: ForgeConf = await loadConf({})\n const taskDescriptor = forge.tasks[descriptorName as keyof typeof forge.tasks]\n const projectName = forge.project.name\n\n if (taskDescriptor === undefined) {\n throw new Error('Task is not defined on forge.json')\n }\n\n // Try to load profile, but continue if not found\n let profile = null\n try {\n profile = await loadCurrentProfile({})\n } catch (error) {\n // Profile not found or not configured, continue without it\n console.log('No profile found, logs will not be sent to remote API')\n }\n\n // Verify if log folder exists\n const logFolderPath = path.join(process.cwd(), forge.paths.logs)\n const logFolderExists = await verifyLogFolder(logFolderPath)\n if (!logFolderExists) {\n throw new Error(`Log folder \"${logFolderPath}\" does not exist`)\n }\n\n // Prepare paths\n const logsPath = path.join(logFolderPath, descriptorName)\n const entryPoint = path.join(process.cwd(), taskDescriptor.path)\n const buildsPath = await ensureBuildsFolder()\n const outputFile = path.join(buildsPath, `${descriptorName}.js`)\n\n // Bundle the task\n await bundleCreate({\n entryPoint,\n outputFile\n })\n\n // Load the bundled task\n const bundle = await bundleLoad({\n bundlePath: outputFile\n })\n\n // Get the task handler\n const task = bundle[taskDescriptor.handler]\n\n if (!task) {\n throw new Error(`Handler \"${taskDescriptor.handler}\" not found in bundle`)\n }\n\n // Setup record tape\n let tape = new RecordTape({\n path: logsPath\n })\n\n // load record tape\n try {\n await tape.load()\n\n // Need to figure out how to handle the log length\n // and other options for the RecordTape\n // For now, we'll just keep the implementation simple\n const maxLogLength = 9\n const log = tape.getLog()\n\n if (log.length > maxLogLength) {\n const newTape = new RecordTape({\n path: logsPath,\n log: log.slice(-maxLogLength)\n })\n\n tape = newTape\n }\n } catch (_error) {\n // if the tape is not found, create a new one on saving\n }\n\n // Run the task with provided arguments\n const [result, error, record] = await task.safeRun(args)\n const logItem = tape.push(descriptorName, record, {\n environment: 'cli'\n })\n await tape.save()\n\n if (profile) {\n try {\n await sendLogToAPI(profile, projectName, descriptorName, logItem)\n } catch (e) {\n console.error('Failed to send log to API:', e)\n }\n }\n\n if (error) {\n throw error\n }\n\n return result\n }\n)\n","error":null}],"writeFile":[{"input":["/Users/danielzavaladlvega/.forge/task:run.task-fingerprint.json","{\n \"taskFingerprint\": {\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"descriptorName\": {\n \"type\": \"string\"\n },\n \"args\": {\n \"type\": \"unknown\"\n }\n }\n },\n \"outputType\": {\n \"type\": \"any\"\n },\n \"boundaries\": [\n \"loadConf\",\n \"loadCurrentProfile\",\n \"bundleCreate\",\n \"bundleLoad\",\n \"verifyLogFolder\",\n \"ensureBuildsFolder\",\n \"sendLogToAPI\"\n ]\n }\n}"],"output":null,"error":null}],"ensureForgeFolder":[{"input":[],"output":"/Users/danielzavaladlvega/.forge","error":null}]},"context":{"environment":"cli"}}
8
- {"name":"task:fingerprint","type":"success","input":{"descriptorName":"task:run"},"output":{"taskName":"task:run","fingerprint":{"inputSchema":{"type":"object","properties":{"descriptorName":{"type":"string"},"args":{"type":"unknown"}}},"outputType":{"type":"unknown"},"boundaries":["loadConf","loadCurrentProfile","bundleCreate","bundleLoad","verifyLogFolder","ensureBuildsFolder","sendLogToAPI"]},"fingerprintFile":"/Users/danielzavaladlvega/.forge/task:run.task-fingerprint.json","analysis":{"inputSchemaProps":["descriptorName","args"],"boundaryCount":7,"hasDescription":false,"outputType":"unknown"}},"boundaries":{"getCwd":[{"input":[],"output":"/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/cli","error":null}],"loadConf":[{"input":[{}],"output":{"project":{"name":"forge-cli"},"paths":{"logs":"logs/","fixtures":"fixtures","tasks":"src/tasks/","runners":"src/runners/","tests":"src/tests/"},"infra":{"region":"us-west-2","bucket":""},"tasks":{"bundle:create":{"path":"src/tasks/bundle/create.ts","handler":"create"},"bundle:load":{"path":"src/tasks/bundle/load.ts","handler":"load"},"task:run":{"path":"src/tasks/task/run.ts","handler":"run"},"task:remove":{"path":"src/tasks/task/remove.ts","handler":"remove"},"conf:info":{"path":"src/tasks/conf/info.ts","handler":"info"},"runner:create":{"path":"src/tasks/runner/create.ts","handler":"create"},"runner:remove":{"path":"src/tasks/runner/remove.ts","handler":"remove"},"runner:bundle":{"path":"src/tasks/runner/bundle.ts","handler":"bundle"},"task:publish":{"path":"src/tasks/task/publish.ts","handler":"publish"},"task:download":{"path":"src/tasks/task/download.ts","handler":"download"},"auth:add":{"path":"src/tasks/auth/add.ts","handler":"add"},"auth:load":{"path":"src/tasks/auth/load.ts","handler":"load"},"auth:loadCurrent":{"path":"src/tasks/auth/loadCurrent.ts","handler":"loadCurrent"},"auth:switch":{"path":"src/tasks/auth/switch.ts","handler":"switchProfile"},"auth:list":{"path":"src/tasks/auth/list.ts","handler":"list"},"auth:remove":{"path":"src/tasks/auth/remove.ts","handler":"remove"},"task:replay":{"path":"src/tasks/task/replay.ts","handler":"replay"},"fixture:download":{"path":"src/tasks/fixture/download.ts","handler":"download"},"bundle:zip":{"path":"src/tasks/bundle/zip.ts","handler":"zip"},"task:list":{"path":"src/tasks/task/list.ts","handler":"list"},"task:describe":{"path":"src/tasks/task/describe.ts","handler":"describe"},"task:fingerprint":{"path":"src/tasks/task/fingerprint.ts","handler":"fingerprint"},"bundle:fingerprint":{"path":"src/tasks/bundle/fingerprint.ts","handler":"fingerprint"}},"runners":{}},"error":null}],"readFile":[{"input":["/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/cli/src/tasks/task/run.ts"],"output":"// TASK: run\n// Run this task with:\n// shadow-cli task:run\n\nimport path from 'path'\nimport fs from 'fs/promises'\nimport os from 'os'\nimport axios from 'axios'\n\nimport { createTask } from '@forgehive/task'\nimport { Schema } from '@forgehive/schema'\nimport { RecordTape } from '@forgehive/record-tape'\n\nimport { create as bundleCreate } from '../bundle/create'\nimport { load as bundleLoad } from '../bundle/load'\nimport { load as loadConf } from '../conf/load'\nimport { loadCurrent as loadCurrentProfile } from '../auth/loadCurrent'\nimport { type ForgeConf, type Profile } from '../types'\n\n// For now, we'll use a simple schema without the record type\n// TODO: Use Schema.record once it's properly built and available\nconst schema = new Schema({\n descriptorName: Schema.string(),\n args: Schema.mixedRecord()\n // args will be passed directly without schema validation for now\n})\n\nconst boundaries = {\n loadConf: loadConf.asBoundary(),\n loadCurrentProfile: loadCurrentProfile.asBoundary(),\n bundleCreate: bundleCreate.asBoundary(),\n bundleLoad: bundleLoad.asBoundary(),\n verifyLogFolder: async (logsPath: string): Promise<boolean> => {\n // return true if the folder exists\n try {\n await fs.access(logsPath)\n } catch (error) {\n return false\n }\n\n return true\n },\n ensureBuildsFolder: async (): Promise<string> => {\n const buildsPath = path.join(os.homedir(), '.forge', 'builds')\n try {\n await fs.access(buildsPath)\n } catch {\n await fs.mkdir(buildsPath, { recursive: true })\n }\n\n return buildsPath\n },\n sendLogToAPI: async (profile: Profile, projectName: string, taskName: string, logItem: unknown): Promise<boolean> => {\n try {\n const logsUrl = `${profile.url}/api/tasks/log-ingest`\n const authToken = `${profile.apiKey}:${profile.apiSecret}`\n\n await axios.post(logsUrl, {\n projectName,\n taskName,\n logItem: JSON.stringify(logItem)\n }, {\n headers: {\n Authorization: `Bearer ${authToken}`,\n 'Content-Type': 'application/json'\n }\n })\n\n console.log('===============================================')\n console.log('Log sent to API... ', profile.name, profile.url)\n\n return true\n } catch (e) {\n const error = e as Error\n console.error('Failed to send log to API:', error.message)\n return false\n }\n }\n}\n\nexport const run = createTask(\n schema,\n boundaries,\n async function ({ descriptorName, args }, {\n loadConf,\n bundleCreate,\n bundleLoad,\n verifyLogFolder,\n ensureBuildsFolder,\n loadCurrentProfile,\n sendLogToAPI\n }) {\n // Load forge configuration\n const forge: ForgeConf = await loadConf({})\n const taskDescriptor = forge.tasks[descriptorName as keyof typeof forge.tasks]\n const projectName = forge.project.name\n\n if (taskDescriptor === undefined) {\n throw new Error('Task is not defined on forge.json')\n }\n\n // Try to load profile, but continue if not found\n let profile = null\n try {\n profile = await loadCurrentProfile({})\n } catch (error) {\n // Profile not found or not configured, continue without it\n console.log('No profile found, logs will not be sent to remote API')\n }\n\n // Verify if log folder exists\n const logFolderPath = path.join(process.cwd(), forge.paths.logs)\n const logFolderExists = await verifyLogFolder(logFolderPath)\n if (!logFolderExists) {\n throw new Error(`Log folder \"${logFolderPath}\" does not exist`)\n }\n\n // Prepare paths\n const logsPath = path.join(logFolderPath, descriptorName)\n const entryPoint = path.join(process.cwd(), taskDescriptor.path)\n const buildsPath = await ensureBuildsFolder()\n const outputFile = path.join(buildsPath, `${descriptorName}.js`)\n\n // Bundle the task\n await bundleCreate({\n entryPoint,\n outputFile\n })\n\n // Load the bundled task\n const bundle = await bundleLoad({\n bundlePath: outputFile\n })\n\n // Get the task handler\n const task = bundle[taskDescriptor.handler]\n\n if (!task) {\n throw new Error(`Handler \"${taskDescriptor.handler}\" not found in bundle`)\n }\n\n // Setup record tape\n let tape = new RecordTape({\n path: logsPath\n })\n\n // load record tape\n try {\n await tape.load()\n\n // Need to figure out how to handle the log length\n // and other options for the RecordTape\n // For now, we'll just keep the implementation simple\n const maxLogLength = 9\n const log = tape.getLog()\n\n if (log.length > maxLogLength) {\n const newTape = new RecordTape({\n path: logsPath,\n log: log.slice(-maxLogLength)\n })\n\n tape = newTape\n }\n } catch (_error) {\n // if the tape is not found, create a new one on saving\n }\n\n // Run the task with provided arguments\n const [result, error, record] = await task.safeRun(args)\n const logItem = tape.push(descriptorName, record, {\n environment: 'cli'\n })\n await tape.save()\n\n if (profile) {\n try {\n await sendLogToAPI(profile, projectName, descriptorName, logItem)\n } catch (e) {\n console.error('Failed to send log to API:', e)\n }\n }\n\n if (error) {\n throw error\n }\n\n return result\n }\n)\n","error":null}],"writeFile":[{"input":["/Users/danielzavaladlvega/.forge/task:run.task-fingerprint.json","{\n \"taskFingerprint\": {\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"descriptorName\": {\n \"type\": \"string\"\n },\n \"args\": {\n \"type\": \"unknown\"\n }\n }\n },\n \"outputType\": {\n \"type\": \"unknown\"\n },\n \"boundaries\": [\n \"loadConf\",\n \"loadCurrentProfile\",\n \"bundleCreate\",\n \"bundleLoad\",\n \"verifyLogFolder\",\n \"ensureBuildsFolder\",\n \"sendLogToAPI\"\n ]\n }\n}"],"output":null,"error":null}],"ensureForgeFolder":[{"input":[],"output":"/Users/danielzavaladlvega/.forge","error":null}]},"context":{"environment":"cli"}}
9
- {"name":"task:fingerprint","type":"error","input":{"descriptorName":"task:createTask"},"error":"Task \"task:createTask\" is not defined in forge.json","boundaries":{"getCwd":[{"input":[],"output":"/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/cli","error":null}],"loadConf":[{"input":[{}],"output":{"project":{"name":"forge-cli"},"paths":{"logs":"logs/","fixtures":"fixtures","tasks":"src/tasks/","runners":"src/runners/","tests":"src/tests/"},"infra":{"region":"us-west-2","bucket":""},"tasks":{"bundle:create":{"path":"src/tasks/bundle/create.ts","handler":"create"},"bundle:load":{"path":"src/tasks/bundle/load.ts","handler":"load"},"task:run":{"path":"src/tasks/task/run.ts","handler":"run"},"task:remove":{"path":"src/tasks/task/remove.ts","handler":"remove"},"conf:info":{"path":"src/tasks/conf/info.ts","handler":"info"},"runner:create":{"path":"src/tasks/runner/create.ts","handler":"create"},"runner:remove":{"path":"src/tasks/runner/remove.ts","handler":"remove"},"runner:bundle":{"path":"src/tasks/runner/bundle.ts","handler":"bundle"},"task:publish":{"path":"src/tasks/task/publish.ts","handler":"publish"},"task:download":{"path":"src/tasks/task/download.ts","handler":"download"},"auth:add":{"path":"src/tasks/auth/add.ts","handler":"add"},"auth:load":{"path":"src/tasks/auth/load.ts","handler":"load"},"auth:loadCurrent":{"path":"src/tasks/auth/loadCurrent.ts","handler":"loadCurrent"},"auth:switch":{"path":"src/tasks/auth/switch.ts","handler":"switchProfile"},"auth:list":{"path":"src/tasks/auth/list.ts","handler":"list"},"auth:remove":{"path":"src/tasks/auth/remove.ts","handler":"remove"},"task:replay":{"path":"src/tasks/task/replay.ts","handler":"replay"},"fixture:download":{"path":"src/tasks/fixture/download.ts","handler":"download"},"bundle:zip":{"path":"src/tasks/bundle/zip.ts","handler":"zip"},"task:list":{"path":"src/tasks/task/list.ts","handler":"list"},"task:describe":{"path":"src/tasks/task/describe.ts","handler":"describe"},"task:fingerprint":{"path":"src/tasks/task/fingerprint.ts","handler":"fingerprint"},"bundle:fingerprint":{"path":"src/tasks/bundle/fingerprint.ts","handler":"fingerprint"}},"runners":{}},"error":null}],"readFile":[],"writeFile":[],"ensureForgeFolder":[]},"context":{"environment":"cli"}}
10
- {"name":"task:fingerprint","type":"success","input":{"descriptorName":"task:createTask"},"output":{"taskName":"task:createTask","fingerprint":{"inputSchema":{"type":"object","properties":{"descriptorName":{"type":"string"}}},"outputType":{"type":"object","properties":{"taskPath":{"type":"string"},"fileName":{"type":"string"}}},"boundaries":["loadConf","loadTemplate","getCwd","parseTaskName","persistTask","persistConf"]},"fingerprintFile":"/Users/danielzavaladlvega/.forge/task:createTask.task-fingerprint.json","analysis":{"inputSchemaProps":["descriptorName"],"boundaryCount":6,"hasDescription":false,"outputType":"object"}},"boundaries":{"getCwd":[{"input":[],"output":"/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/cli","error":null}],"loadConf":[{"input":[{}],"output":{"project":{"name":"forge-cli"},"paths":{"logs":"logs/","fixtures":"fixtures","tasks":"src/tasks/","runners":"src/runners/","tests":"src/tests/"},"infra":{"region":"us-west-2","bucket":""},"tasks":{"task:createTask":{"path":"src/tasks/task/createTask.ts","handler":"createTask"},"bundle:create":{"path":"src/tasks/bundle/create.ts","handler":"create"},"bundle:load":{"path":"src/tasks/bundle/load.ts","handler":"load"},"task:run":{"path":"src/tasks/task/run.ts","handler":"run"},"task:remove":{"path":"src/tasks/task/remove.ts","handler":"remove"},"conf:info":{"path":"src/tasks/conf/info.ts","handler":"info"},"runner:create":{"path":"src/tasks/runner/create.ts","handler":"create"},"runner:remove":{"path":"src/tasks/runner/remove.ts","handler":"remove"},"runner:bundle":{"path":"src/tasks/runner/bundle.ts","handler":"bundle"},"task:publish":{"path":"src/tasks/task/publish.ts","handler":"publish"},"task:download":{"path":"src/tasks/task/download.ts","handler":"download"},"auth:add":{"path":"src/tasks/auth/add.ts","handler":"add"},"auth:load":{"path":"src/tasks/auth/load.ts","handler":"load"},"auth:loadCurrent":{"path":"src/tasks/auth/loadCurrent.ts","handler":"loadCurrent"},"auth:switch":{"path":"src/tasks/auth/switch.ts","handler":"switchProfile"},"auth:list":{"path":"src/tasks/auth/list.ts","handler":"list"},"auth:remove":{"path":"src/tasks/auth/remove.ts","handler":"remove"},"task:replay":{"path":"src/tasks/task/replay.ts","handler":"replay"},"fixture:download":{"path":"src/tasks/fixture/download.ts","handler":"download"},"bundle:zip":{"path":"src/tasks/bundle/zip.ts","handler":"zip"},"task:list":{"path":"src/tasks/task/list.ts","handler":"list"},"task:describe":{"path":"src/tasks/task/describe.ts","handler":"describe"},"task:fingerprint":{"path":"src/tasks/task/fingerprint.ts","handler":"fingerprint"},"bundle:fingerprint":{"path":"src/tasks/bundle/fingerprint.ts","handler":"fingerprint"}},"runners":{}},"error":null}],"readFile":[{"input":["/Users/danielzavaladlvega/forgehive/forge-mono-repo/apps/cli/src/tasks/task/createTask.ts"],"output":"import { createTask } from '@forgehive/task'\nimport { Schema } from '@forgehive/schema'\n\nimport Handlebars from 'handlebars'\nimport path from 'path'\nimport fs from 'fs/promises'\nimport { camelCase } from '../../utils/camelCase'\n\nimport { load } from '../conf/load'\nimport { type TaskName, type ForgeConf } from '../types'\n\n// Define the template content directly in the code\n// This eliminates the need to find and load an external file\nconst TASK_TEMPLATE = `// TASK: {{ taskName }}\n// Run this task with:\n// forge task:run {{ taskDescriptor }}\n\nimport { createTask } from '@forgehive/task'\nimport { Schema } from '@forgehive/schema'\n\nconst description = 'Add task description here'\n\nconst schema = new Schema({\n // Add your schema definitions here\n // example: myParam: Schema.string()\n})\n\nconst boundaries = {\n // Add your boundary functions here\n // example: readFile: async (path: string) => fs.readFile(path, 'utf-8')\n}\n\nexport const {{ taskName }} = createTask(\n schema,\n boundaries,\n async function (argv, boundaries) {\n console.log('input:', argv)\n console.log('boundaries:', boundaries)\n // Your task implementation goes here\n const status = { status: 'Ok' }\n\n return status\n }\n)\n\n{{ taskName }}.setDescription(description)\n`\n\nconst schema = new Schema({\n descriptorName: Schema.string()\n})\n\nconst boundaries = {\n // Load boundaries\n loadConf: load.asBoundary(),\n loadTemplate: async (): Promise<string> => {\n return TASK_TEMPLATE\n },\n getCwd: async (): Promise<string> => {\n return process.cwd()\n },\n parseTaskName: async (taskDescriptor: string): Promise<TaskName> => {\n const res: string[] = taskDescriptor.split(':')\n\n if (res.length === 1) {\n return {\n descriptor: `${camelCase(res[0])}`,\n taskName: `${camelCase(res[0])}`,\n fileName: `${camelCase(res[0])}.ts`\n }\n }\n\n return {\n dir: res[0],\n descriptor: `${res[0]}:${camelCase(res[1])}`,\n taskName: `${camelCase(res[1])}`,\n fileName: `${camelCase(res[1])}.ts`\n }\n },\n\n // Persist boundaries\n persistTask: async (dir: string, fileName: string, content: string, cwd: string): Promise<{ path: string }> => {\n const dirPath = path.resolve(cwd, dir)\n const taskPath = path.resolve(dirPath, fileName)\n\n let err\n try {\n await fs.stat(taskPath)\n } catch (e) {\n err = e\n }\n\n if (err === undefined) {\n throw new Error(`File '${taskPath}' already exists.`)\n }\n\n await fs.mkdir(dir, { recursive: true })\n await fs.writeFile(taskPath, content, 'utf-8')\n\n return {\n path: taskPath.toString()\n }\n },\n persistConf: async (forge: ForgeConf, cwd: string): Promise<void> => {\n const forgePath = path.join(cwd, 'forge.json')\n await fs.writeFile(forgePath, JSON.stringify(forge, null, 2))\n }\n}\n\nexport const createTaskCommand = createTask(\n schema,\n boundaries,\n async function ({ descriptorName }, {\n loadTemplate,\n persistTask,\n loadConf,\n persistConf,\n parseTaskName,\n getCwd\n }) {\n const { taskName, fileName, descriptor, dir } = await parseTaskName(descriptorName)\n const cwd = await getCwd()\n\n const forge = await loadConf({})\n let taskPath: string = forge.paths.tasks\n\n if (dir !== undefined) {\n taskPath = path.join(taskPath, dir)\n }\n\n console.log(`\n ==================================================\n Starting task creation!\n Creating: ${taskName}\n Dir: ${dir ?? ''}\n Into: ${taskPath}\n ==================================================\n `)\n\n const template = await loadTemplate()\n const comp = Handlebars.compile(template)\n const content = comp({\n taskName,\n taskDescriptor: descriptor\n })\n\n await persistTask(taskPath, fileName, content, cwd)\n\n if (forge.tasks === undefined) {\n forge.tasks = {}\n }\n\n forge.tasks[descriptor] = {\n path: `${taskPath}/${fileName}`,\n handler: taskName\n }\n\n await persistConf(forge, cwd)\n\n return { taskPath, fileName }\n }\n)\n","error":null}],"writeFile":[{"input":["/Users/danielzavaladlvega/.forge/task:createTask.task-fingerprint.json","{\n \"taskFingerprint\": {\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"descriptorName\": {\n \"type\": \"string\"\n }\n }\n },\n \"outputType\": {\n \"type\": \"object\",\n \"properties\": {\n \"taskPath\": {\n \"type\": \"string\"\n },\n \"fileName\": {\n \"type\": \"string\"\n }\n }\n },\n \"boundaries\": [\n \"loadConf\",\n \"loadTemplate\",\n \"getCwd\",\n \"parseTaskName\",\n \"persistTask\",\n \"persistConf\"\n ]\n }\n}"],"output":null,"error":null}],"ensureForgeFolder":[{"input":[],"output":"/Users/danielzavaladlvega/.forge","error":null}]},"context":{"environment":"cli"}}
@@ -1 +0,0 @@
1
- {"input":{},"boundaries":{"loadConf":[{"input":[{}],"output":{"project":{"name":"forge-cli"},"paths":{"logs":"logs/","fixtures":"fixtures","fingerprints":"fingerprints/","tasks":"src/tasks/","runners":"src/runners/","tests":"src/tests/"},"infra":{"region":"us-west-2","bucket":""},"tasks":{"task:createTask":{"path":"src/tasks/task/createTask.ts","handler":"createTask"},"bundle:create":{"path":"src/tasks/bundle/create.ts","handler":"create"},"bundle:load":{"path":"src/tasks/bundle/load.ts","handler":"load"},"task:run":{"path":"src/tasks/task/run.ts","handler":"run"},"task:remove":{"path":"src/tasks/task/remove.ts","handler":"remove"},"conf:info":{"path":"src/tasks/conf/info.ts","handler":"info"},"runner:create":{"path":"src/tasks/runner/create.ts","handler":"create"},"runner:remove":{"path":"src/tasks/runner/remove.ts","handler":"remove"},"runner:bundle":{"path":"src/tasks/runner/bundle.ts","handler":"bundle"},"task:publish":{"path":"src/tasks/task/publish.ts","handler":"publish"},"task:download":{"path":"src/tasks/task/download.ts","handler":"download"},"auth:add":{"path":"src/tasks/auth/add.ts","handler":"add"},"auth:load":{"path":"src/tasks/auth/load.ts","handler":"load"},"auth:loadCurrent":{"path":"src/tasks/auth/loadCurrent.ts","handler":"loadCurrent"},"auth:switch":{"path":"src/tasks/auth/switch.ts","handler":"switchProfile"},"auth:list":{"path":"src/tasks/auth/list.ts","handler":"list"},"auth:remove":{"path":"src/tasks/auth/remove.ts","handler":"remove"},"task:replay":{"path":"src/tasks/task/replay.ts","handler":"replay"},"fixture:download":{"path":"src/tasks/fixture/download.ts","handler":"download"},"bundle:zip":{"path":"src/tasks/bundle/zip.ts","handler":"zip"},"task:list":{"path":"src/tasks/task/list.ts","handler":"list"},"task:describe":{"path":"src/tasks/task/describe.ts","handler":"describe"},"task:fingerprint":{"path":"src/tasks/task/fingerprint.ts","handler":"fingerprint"},"bundle:fingerprint":{"path":"src/tasks/bundle/fingerprint.ts","handler":"fingerprint"},"task:invoke":{"path":"src/tasks/task/invoke.ts","handler":"invoke"},"docs:download":{"path":"src/tasks/docs/download.ts","handler":"download"},"project:create":{"path":"src/tasks/project/create.ts","handler":"create"},"project:link":{"path":"src/tasks/project/link.ts","handler":"link","uuid":"cb9f82e1-d397-46d9-9f0d-2b0e3becbfa1"},"project:unlink":{"path":"src/tasks/project/unlink.ts","handler":"unlink","uuid":"414d37de-793c-4d01-899d-69515f5e0948"}},"runners":{}},"timing":{"startTime":1755627445111,"endTime":1755627445112,"duration":1}}]},"metadata":{"environment":"cli"},"metrics":[],"type":"success","output":{"taskCount":29},"timing":{"startTime":1755627445111,"endTime":1755627445112,"duration":1}}
@@ -1 +0,0 @@
1
- {"input":{},"boundaries":{},"taskName":"test:guidance","metadata":{"environment":"cli"},"metrics":[],"type":"success","output":{},"timing":{"startTime":1755627427045,"endTime":1755627427045,"duration":0}}
@@ -1 +0,0 @@
1
- {"uuid":"0198bfeb-69c0-77e8-882b-a76dcb9300f9","input":{},"boundaries":{},"taskName":"test:uuid","metadata":{"environment":"cli"},"metrics":[],"type":"success","output":{},"timing":{"startTime":1755566533056,"endTime":1755566533057,"duration":1}}
@@ -1 +0,0 @@
1
- {"input":{},"boundaries":{},"taskName":"test:uuidCheck","metadata":{"environment":"cli"},"metrics":[],"type":"success","output":{},"timing":{"startTime":1755567492456,"endTime":1755567492456,"duration":0}}