@forgehive/forge-cli 0.2.14 → 0.3.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.
- package/dist/runner.js +3 -1
- package/dist/tasks/auth/add.js +23 -19
- package/dist/tasks/auth/list.js +20 -16
- package/dist/tasks/auth/load.js +19 -15
- package/dist/tasks/auth/loadCurrent.js +13 -9
- package/dist/tasks/auth/remove.js +30 -26
- package/dist/tasks/auth/switch.js +19 -15
- package/dist/tasks/bundle/create.js +16 -12
- package/dist/tasks/bundle/fingerprint.d.ts +36 -0
- package/dist/tasks/bundle/fingerprint.js +164 -0
- package/dist/tasks/bundle/load.js +9 -5
- package/dist/tasks/bundle/zip.js +49 -45
- package/dist/tasks/conf/info.js +23 -19
- package/dist/tasks/conf/load.js +8 -4
- package/dist/tasks/fixture/download.js +40 -36
- package/dist/tasks/init.js +35 -31
- package/dist/tasks/runner/bundle.js +34 -30
- package/dist/tasks/runner/create.js +28 -24
- package/dist/tasks/runner/remove.js +22 -18
- package/dist/tasks/task/createTask.js +35 -28
- package/dist/tasks/task/describe.js +85 -81
- package/dist/tasks/task/download.js +63 -59
- package/dist/tasks/task/fingerprint.d.ts +26 -0
- package/dist/tasks/task/fingerprint.js +87 -0
- package/dist/tasks/task/list.js +27 -23
- package/dist/tasks/task/publish.js +72 -68
- package/dist/tasks/task/remove.js +24 -20
- package/dist/tasks/task/replay.js +94 -90
- package/dist/tasks/task/run.js +78 -79
- package/dist/test/tasks/create.test.js +6 -5
- package/dist/utils/taskAnalysis.d.ts +21 -0
- package/dist/utils/taskAnalysis.js +380 -0
- package/forge.json +12 -0
- package/logs/task:fingerprint.log +10 -0
- package/package.json +7 -7
- package/specs/fingerprint.md +380 -0
- package/src/runner.ts +3 -1
- package/src/tasks/README.md +13 -13
- package/src/tasks/auth/add.ts +3 -3
- package/src/tasks/auth/list.ts +3 -3
- package/src/tasks/auth/load.ts +3 -3
- package/src/tasks/auth/loadCurrent.ts +3 -3
- package/src/tasks/auth/remove.ts +3 -3
- package/src/tasks/auth/switch.ts +3 -3
- package/src/tasks/bundle/README.md +7 -7
- package/src/tasks/bundle/create.ts +4 -4
- package/src/tasks/bundle/fingerprint.ts +218 -0
- package/src/tasks/bundle/load.ts +4 -4
- package/src/tasks/bundle/zip.ts +3 -3
- package/src/tasks/conf/info.ts +3 -3
- package/src/tasks/conf/load.ts +3 -3
- package/src/tasks/fixture/download.ts +3 -3
- package/src/tasks/init.ts +3 -3
- package/src/tasks/runner/bundle.ts +3 -3
- package/src/tasks/runner/create.ts +3 -3
- package/src/tasks/runner/remove.ts +3 -3
- package/src/tasks/task/createTask.ts +10 -7
- package/src/tasks/task/describe.ts +3 -3
- package/src/tasks/task/download.ts +3 -3
- package/src/tasks/task/fingerprint.ts +107 -0
- package/src/tasks/task/list.ts +3 -3
- package/src/tasks/task/publish.ts +3 -3
- package/src/tasks/task/remove.ts +3 -3
- package/src/tasks/task/replay.ts +3 -3
- package/src/tasks/task/run.ts +12 -18
- package/src/test/tasks/create.test.ts +9 -9
- package/src/utils/taskAnalysis.ts +419 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
// TASK: fingerprint
|
|
2
|
+
// Run this task with:
|
|
3
|
+
// forge task:run bundle:fingerprint --descriptorName task-name
|
|
4
|
+
|
|
5
|
+
import { createTask } from '@forgehive/task'
|
|
6
|
+
import { Schema } from '@forgehive/schema'
|
|
7
|
+
import esbuild from 'esbuild'
|
|
8
|
+
import fs from 'fs/promises'
|
|
9
|
+
import path from 'path'
|
|
10
|
+
import os from 'os'
|
|
11
|
+
|
|
12
|
+
import { load as loadConf } from '../conf/load'
|
|
13
|
+
import { analyzeTaskFile, TaskFingerprintOutput } from '../../utils/taskAnalysis'
|
|
14
|
+
|
|
15
|
+
interface TaskFingerprint {
|
|
16
|
+
name: string
|
|
17
|
+
description?: string
|
|
18
|
+
location: {
|
|
19
|
+
file: string
|
|
20
|
+
line: number
|
|
21
|
+
column: number
|
|
22
|
+
}
|
|
23
|
+
inputSchema: TaskFingerprintOutput['inputSchema']
|
|
24
|
+
outputType: TaskFingerprintOutput['outputType']
|
|
25
|
+
boundaries: string[]
|
|
26
|
+
hash: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface EsbuildResultWithFingerprints extends esbuild.BuildResult {
|
|
30
|
+
fingerprints?: TaskFingerprint[]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface FingerprintResult {
|
|
34
|
+
tasks: TaskFingerprintOutput[]
|
|
35
|
+
buildInfo: {
|
|
36
|
+
entryPoint: string
|
|
37
|
+
outputFile: string
|
|
38
|
+
fingerprintsFile?: string
|
|
39
|
+
totalTasks: number
|
|
40
|
+
buildTimestamp: string
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const description = 'Generate task bundle with comprehensive fingerprinting and type extraction'
|
|
45
|
+
|
|
46
|
+
const schema = new Schema({
|
|
47
|
+
descriptorName: Schema.string(),
|
|
48
|
+
filePath: Schema.string().optional()
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const boundaries = {
|
|
52
|
+
getCwd: async (): Promise<string> => {
|
|
53
|
+
return process.cwd()
|
|
54
|
+
},
|
|
55
|
+
loadConf: loadConf.asBoundary(),
|
|
56
|
+
readFile: async (filePath: string): Promise<string> => {
|
|
57
|
+
return fs.readFile(filePath, 'utf-8')
|
|
58
|
+
},
|
|
59
|
+
writeFile: async (filePath: string, content: string): Promise<void> => {
|
|
60
|
+
return fs.writeFile(filePath, content)
|
|
61
|
+
},
|
|
62
|
+
ensureForgeFolder: async (): Promise<string> => {
|
|
63
|
+
const forgePath = path.join(os.homedir(), '.forge')
|
|
64
|
+
try {
|
|
65
|
+
await fs.access(forgePath)
|
|
66
|
+
} catch {
|
|
67
|
+
await fs.mkdir(forgePath, { recursive: true })
|
|
68
|
+
}
|
|
69
|
+
return forgePath
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// esbuild plugin for fingerprinting
|
|
74
|
+
function taskFingerprintPlugin(): esbuild.Plugin {
|
|
75
|
+
return {
|
|
76
|
+
name: 'task-fingerprint',
|
|
77
|
+
setup(build): void {
|
|
78
|
+
const fingerprints: TaskFingerprint[] = []
|
|
79
|
+
|
|
80
|
+
build.onLoad({ filter: /\.ts$/ }, async (args) => {
|
|
81
|
+
const sourceCode = await fs.readFile(args.path, 'utf-8')
|
|
82
|
+
|
|
83
|
+
// Only analyze files that contain createTask
|
|
84
|
+
if (sourceCode.includes('createTask')) {
|
|
85
|
+
const taskFingerprint = analyzeTaskFile(sourceCode, args.path)
|
|
86
|
+
if (taskFingerprint) {
|
|
87
|
+
// Convert to full TaskFingerprint for plugin compatibility
|
|
88
|
+
const fullFingerprint: TaskFingerprint = {
|
|
89
|
+
name: path.basename(args.path, '.ts'),
|
|
90
|
+
description: taskFingerprint.description,
|
|
91
|
+
location: {
|
|
92
|
+
file: args.path,
|
|
93
|
+
line: 1,
|
|
94
|
+
column: 1
|
|
95
|
+
},
|
|
96
|
+
inputSchema: taskFingerprint.inputSchema,
|
|
97
|
+
outputType: taskFingerprint.outputType,
|
|
98
|
+
boundaries: taskFingerprint.boundaries,
|
|
99
|
+
hash: 'generated-hash'
|
|
100
|
+
}
|
|
101
|
+
fingerprints.push(fullFingerprint)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return null // Let esbuild handle the file normally
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
build.onEnd((result): void => {
|
|
109
|
+
// Store fingerprints for later use
|
|
110
|
+
(result as EsbuildResultWithFingerprints).fingerprints = fingerprints
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const fingerprint = createTask({
|
|
117
|
+
schema,
|
|
118
|
+
boundaries,
|
|
119
|
+
fn: async function ({ descriptorName, filePath }, {
|
|
120
|
+
getCwd,
|
|
121
|
+
loadConf,
|
|
122
|
+
readFile,
|
|
123
|
+
writeFile,
|
|
124
|
+
ensureForgeFolder
|
|
125
|
+
}) {
|
|
126
|
+
// If filePath is provided, analyze that file directly and return JSON
|
|
127
|
+
if (filePath) {
|
|
128
|
+
console.log(`Analyzing task file: ${filePath}`)
|
|
129
|
+
const sourceCode = await readFile(filePath)
|
|
130
|
+
const fingerprintOutput = analyzeTaskFile(sourceCode, filePath)
|
|
131
|
+
|
|
132
|
+
if (!fingerprintOutput) {
|
|
133
|
+
throw new Error('Could not extract fingerprint from task file: ' + filePath)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
taskFingerprint: fingerprintOutput
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Original bundle logic when no filePath is provided
|
|
142
|
+
const cwd = await getCwd()
|
|
143
|
+
const forgeJson = await loadConf({})
|
|
144
|
+
|
|
145
|
+
const taskDescriptor = forgeJson.tasks[descriptorName as keyof typeof forgeJson.tasks]
|
|
146
|
+
|
|
147
|
+
if (taskDescriptor === undefined) {
|
|
148
|
+
throw new Error(`Task "${descriptorName}" is not defined in forge.json`)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const entryPoint = path.join(cwd, taskDescriptor.path)
|
|
152
|
+
const forgePath = await ensureForgeFolder()
|
|
153
|
+
const outputFile = path.join(forgePath, `${descriptorName}.js`)
|
|
154
|
+
const fingerprintsFile = path.join(forgePath, `${descriptorName}.fingerprints.json`)
|
|
155
|
+
|
|
156
|
+
console.log(`Generating bundle with fingerprints for task: ${descriptorName}`)
|
|
157
|
+
console.log(`Entry point: ${entryPoint}`)
|
|
158
|
+
console.log(`Output: ${outputFile}`)
|
|
159
|
+
|
|
160
|
+
// Build with fingerprinting plugin
|
|
161
|
+
const result = await esbuild.build({
|
|
162
|
+
entryPoints: [entryPoint],
|
|
163
|
+
outfile: outputFile,
|
|
164
|
+
bundle: true,
|
|
165
|
+
minify: true,
|
|
166
|
+
platform: 'node',
|
|
167
|
+
sourcemap: true,
|
|
168
|
+
plugins: [taskFingerprintPlugin()],
|
|
169
|
+
metafile: true
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
// Extract fingerprints from build result
|
|
173
|
+
const taskFingerprints = (result as EsbuildResultWithFingerprints).fingerprints || []
|
|
174
|
+
|
|
175
|
+
// Convert to simplified output format (remove name, location, hash)
|
|
176
|
+
const simplifiedFingerprints: TaskFingerprintOutput[] = taskFingerprints.map((fp: TaskFingerprint) => ({
|
|
177
|
+
description: fp.description,
|
|
178
|
+
inputSchema: fp.inputSchema,
|
|
179
|
+
outputType: fp.outputType,
|
|
180
|
+
boundaries: fp.boundaries
|
|
181
|
+
}))
|
|
182
|
+
|
|
183
|
+
// Create fingerprint result
|
|
184
|
+
const fingerprintResult: FingerprintResult = {
|
|
185
|
+
tasks: simplifiedFingerprints,
|
|
186
|
+
buildInfo: {
|
|
187
|
+
entryPoint,
|
|
188
|
+
outputFile,
|
|
189
|
+
fingerprintsFile,
|
|
190
|
+
totalTasks: simplifiedFingerprints.length,
|
|
191
|
+
buildTimestamp: new Date().toISOString()
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Write fingerprints to file
|
|
196
|
+
await writeFile(fingerprintsFile, JSON.stringify(fingerprintResult, null, 2))
|
|
197
|
+
|
|
198
|
+
console.log(`Generated ${taskFingerprints.length} task fingerprints`)
|
|
199
|
+
console.log(`Fingerprints saved to: ${fingerprintsFile}`)
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
outputFile,
|
|
203
|
+
fingerprintsFile,
|
|
204
|
+
taskFingerprints: {
|
|
205
|
+
totalTasks: taskFingerprints.length,
|
|
206
|
+
tasks: taskFingerprints.map((fp: TaskFingerprint) => ({
|
|
207
|
+
name: fp.name,
|
|
208
|
+
inputType: fp.inputSchema.type,
|
|
209
|
+
outputType: fp.outputType.type,
|
|
210
|
+
boundaryCount: fp.boundaries.length,
|
|
211
|
+
hash: fp.hash
|
|
212
|
+
}))
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
fingerprint.setDescription(description)
|
package/src/tasks/bundle/load.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// TASK: load
|
|
2
2
|
// Run this task with:
|
|
3
|
-
//
|
|
3
|
+
// forge task:run bundle:load
|
|
4
4
|
|
|
5
5
|
import { createTask } from '@forgehive/task'
|
|
6
6
|
import { Schema } from '@forgehive/schema'
|
|
@@ -11,13 +11,13 @@ const schema = new Schema({
|
|
|
11
11
|
|
|
12
12
|
const boundaries = {}
|
|
13
13
|
|
|
14
|
-
export const load = createTask(
|
|
14
|
+
export const load = createTask({
|
|
15
15
|
schema,
|
|
16
16
|
boundaries,
|
|
17
|
-
async function ({ bundlePath }) {
|
|
17
|
+
fn: async function ({ bundlePath }) {
|
|
18
18
|
// Dynamically import the bundle from the specified path
|
|
19
19
|
const bundle = await import(bundlePath)
|
|
20
20
|
|
|
21
21
|
return bundle
|
|
22
22
|
}
|
|
23
|
-
)
|
|
23
|
+
})
|
package/src/tasks/bundle/zip.ts
CHANGED
|
@@ -41,10 +41,10 @@ export const bytesToMB = (bytes: number): string => {
|
|
|
41
41
|
return `${MB.toFixed(2)} MB`
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
export const zip = createTask(
|
|
44
|
+
export const zip = createTask({
|
|
45
45
|
schema,
|
|
46
46
|
boundaries,
|
|
47
|
-
async function ({ dir, input, output }, { createWriteStream, createArchiver, resolvePathDir, fileExists }) {
|
|
47
|
+
fn: async function ({ dir, input, output }, { createWriteStream, createArchiver, resolvePathDir, fileExists }) {
|
|
48
48
|
const outputPath = await resolvePathDir(dir, output)
|
|
49
49
|
const inputPath = await resolvePathDir(dir, input)
|
|
50
50
|
const inputMapPath = inputPath + '.map'
|
|
@@ -104,6 +104,6 @@ export const zip = createTask(
|
|
|
104
104
|
archive.finalize()
|
|
105
105
|
})
|
|
106
106
|
}
|
|
107
|
-
)
|
|
107
|
+
})
|
|
108
108
|
|
|
109
109
|
zip.setDescription(description)
|
package/src/tasks/conf/info.ts
CHANGED
|
@@ -16,10 +16,10 @@ const boundaries = {
|
|
|
16
16
|
loadCurrentProfile: loadCurrentProfile.asBoundary()
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export const info = createTask(
|
|
19
|
+
export const info = createTask({
|
|
20
20
|
schema,
|
|
21
21
|
boundaries,
|
|
22
|
-
async function (_argv, { loadCurrentProfile, readFile }) {
|
|
22
|
+
fn: async function (_argv, { loadCurrentProfile, readFile }) {
|
|
23
23
|
const packageJsonPath = path.join(__dirname, '../../../package.json')
|
|
24
24
|
|
|
25
25
|
const packageJsonContent = await readFile(packageJsonPath)
|
|
@@ -45,4 +45,4 @@ export const info = createTask(
|
|
|
45
45
|
|
|
46
46
|
return info
|
|
47
47
|
}
|
|
48
|
-
)
|
|
48
|
+
})
|
package/src/tasks/conf/load.ts
CHANGED
|
@@ -14,13 +14,13 @@ const boundaries = {
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export const load = createTask(
|
|
17
|
+
export const load = createTask({
|
|
18
18
|
schema,
|
|
19
19
|
boundaries,
|
|
20
|
-
async function (_, { readFile }) {
|
|
20
|
+
fn: async function (_, { readFile }) {
|
|
21
21
|
const forgePath = path.join(process.cwd(), 'forge.json')
|
|
22
22
|
|
|
23
23
|
const content = await readFile(forgePath)
|
|
24
24
|
return JSON.parse(content) as ForgeConf
|
|
25
25
|
}
|
|
26
|
-
)
|
|
26
|
+
})
|
|
@@ -62,10 +62,10 @@ const boundaries = {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
export const download = createTask(
|
|
65
|
+
export const download = createTask({
|
|
66
66
|
schema,
|
|
67
67
|
boundaries,
|
|
68
|
-
async function ({ uuid }, {
|
|
68
|
+
fn: async function ({ uuid }, {
|
|
69
69
|
downloadFixture,
|
|
70
70
|
getCwd,
|
|
71
71
|
persistFixture,
|
|
@@ -131,6 +131,6 @@ forge task:replay ${taskName} --path ${shortPath}
|
|
|
131
131
|
shortPath: shortPath
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
|
-
)
|
|
134
|
+
})
|
|
135
135
|
|
|
136
136
|
download.setDescription(description)
|
package/src/tasks/init.ts
CHANGED
|
@@ -20,10 +20,10 @@ const boundaries = {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
// Create a task with type inference from schema and boundaries
|
|
23
|
-
export const init = createTask(
|
|
23
|
+
export const init = createTask({
|
|
24
24
|
schema,
|
|
25
25
|
boundaries,
|
|
26
|
-
async function (argv, { saveFile, getCwd }) {
|
|
26
|
+
fn: async function (argv, { saveFile, getCwd }) {
|
|
27
27
|
// Handle the dryRun flag
|
|
28
28
|
const isDryRun = Boolean(argv.dryRun)
|
|
29
29
|
|
|
@@ -60,4 +60,4 @@ export const init = createTask(
|
|
|
60
60
|
|
|
61
61
|
return config
|
|
62
62
|
}
|
|
63
|
-
)
|
|
63
|
+
})
|
|
@@ -29,10 +29,10 @@ const boundaries = {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export const bundle = createTask(
|
|
32
|
+
export const bundle = createTask({
|
|
33
33
|
schema,
|
|
34
34
|
boundaries,
|
|
35
|
-
async function ({ runnerName, targetPath }, { loadConf, getCwd, ensureDir }) {
|
|
35
|
+
fn: async function ({ runnerName, targetPath }, { loadConf, getCwd, ensureDir }) {
|
|
36
36
|
// Load forge configuration
|
|
37
37
|
const forge: ForgeConf = await loadConf({})
|
|
38
38
|
const cwd = await getCwd()
|
|
@@ -76,4 +76,4 @@ export const bundle = createTask(
|
|
|
76
76
|
outputFile: path.join(targetPath, `${runnerName}.js`)
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
-
)
|
|
79
|
+
})
|
|
@@ -69,10 +69,10 @@ const boundaries = {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
export const create = createTask(
|
|
72
|
+
export const create = createTask({
|
|
73
73
|
schema,
|
|
74
74
|
boundaries,
|
|
75
|
-
async function ({ runnerName }, {
|
|
75
|
+
fn: async function ({ runnerName }, {
|
|
76
76
|
persistRunner,
|
|
77
77
|
loadConf,
|
|
78
78
|
persistConf,
|
|
@@ -118,4 +118,4 @@ export const create = createTask(
|
|
|
118
118
|
runnerName: formattedName
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
-
)
|
|
121
|
+
})
|
|
@@ -32,10 +32,10 @@ const boundaries = {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export const remove = createTask(
|
|
35
|
+
export const remove = createTask({
|
|
36
36
|
schema,
|
|
37
37
|
boundaries,
|
|
38
|
-
async function ({ runnerName }, {
|
|
38
|
+
fn: async function ({ runnerName }, {
|
|
39
39
|
loadConf,
|
|
40
40
|
getCwd,
|
|
41
41
|
removeRunner,
|
|
@@ -71,4 +71,4 @@ export const remove = createTask(
|
|
|
71
71
|
runnerName: formattedName
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
-
)
|
|
74
|
+
})
|
|
@@ -18,6 +18,7 @@ const TASK_TEMPLATE = `// TASK: {{ taskName }}
|
|
|
18
18
|
import { createTask } from '@forgehive/task'
|
|
19
19
|
import { Schema } from '@forgehive/schema'
|
|
20
20
|
|
|
21
|
+
const name = '{{ taskDescriptor }}'
|
|
21
22
|
const description = 'Add task description here'
|
|
22
23
|
|
|
23
24
|
const schema = new Schema({
|
|
@@ -30,10 +31,12 @@ const boundaries = {
|
|
|
30
31
|
// example: readFile: async (path: string) => fs.readFile(path, 'utf-8')
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
export const {{ taskName }} = createTask(
|
|
34
|
+
export const {{ taskName }} = createTask({
|
|
35
|
+
name,
|
|
36
|
+
description,
|
|
34
37
|
schema,
|
|
35
38
|
boundaries,
|
|
36
|
-
async function (argv, boundaries) {
|
|
39
|
+
fn: async function (argv, boundaries) {
|
|
37
40
|
console.log('input:', argv)
|
|
38
41
|
console.log('boundaries:', boundaries)
|
|
39
42
|
// Your task implementation goes here
|
|
@@ -41,9 +44,8 @@ export const {{ taskName }} = createTask(
|
|
|
41
44
|
|
|
42
45
|
return status
|
|
43
46
|
}
|
|
44
|
-
)
|
|
47
|
+
})
|
|
45
48
|
|
|
46
|
-
{{ taskName }}.setDescription(description)
|
|
47
49
|
`
|
|
48
50
|
|
|
49
51
|
const schema = new Schema({
|
|
@@ -107,10 +109,10 @@ const boundaries = {
|
|
|
107
109
|
}
|
|
108
110
|
}
|
|
109
111
|
|
|
110
|
-
export const createTaskCommand = createTask(
|
|
112
|
+
export const createTaskCommand = createTask({
|
|
111
113
|
schema,
|
|
112
114
|
boundaries,
|
|
113
|
-
async function ({ descriptorName }, {
|
|
115
|
+
fn: async function ({ descriptorName }, {
|
|
114
116
|
loadTemplate,
|
|
115
117
|
persistTask,
|
|
116
118
|
loadConf,
|
|
@@ -132,6 +134,7 @@ export const createTaskCommand = createTask(
|
|
|
132
134
|
==================================================
|
|
133
135
|
Starting task creation!
|
|
134
136
|
Creating: ${taskName}
|
|
137
|
+
Descriptor: ${descriptor}
|
|
135
138
|
Dir: ${dir ?? ''}
|
|
136
139
|
Into: ${taskPath}
|
|
137
140
|
==================================================
|
|
@@ -159,4 +162,4 @@ export const createTaskCommand = createTask(
|
|
|
159
162
|
|
|
160
163
|
return { taskPath, fileName }
|
|
161
164
|
}
|
|
162
|
-
)
|
|
165
|
+
})
|
|
@@ -36,10 +36,10 @@ const boundaries = {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
export const describe = createTask(
|
|
39
|
+
export const describe = createTask({
|
|
40
40
|
schema,
|
|
41
41
|
boundaries,
|
|
42
|
-
async function ({ descriptorName }, {
|
|
42
|
+
fn: async function ({ descriptorName }, {
|
|
43
43
|
loadConf,
|
|
44
44
|
bundleCreate,
|
|
45
45
|
bundleLoad,
|
|
@@ -143,6 +143,6 @@ export const describe = createTask(
|
|
|
143
143
|
boundaries: taskBoundaries ? Object.keys(taskBoundaries) : []
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
|
-
)
|
|
146
|
+
})
|
|
147
147
|
|
|
148
148
|
describe.setDescription(description)
|
|
@@ -90,10 +90,10 @@ const boundaries = {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
export const download = createTask(
|
|
93
|
+
export const download = createTask({
|
|
94
94
|
schema,
|
|
95
95
|
boundaries,
|
|
96
|
-
async function ({ descriptorName, uuid }, {
|
|
96
|
+
fn: async function ({ descriptorName, uuid }, {
|
|
97
97
|
downloadTask,
|
|
98
98
|
getCwd,
|
|
99
99
|
parseTaskName,
|
|
@@ -189,4 +189,4 @@ export const download = createTask(
|
|
|
189
189
|
handler: response.handler
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
|
-
)
|
|
192
|
+
})
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// TASK: fingerprint
|
|
2
|
+
// Run this task with:
|
|
3
|
+
// forge task:run task:fingerprint --descriptorName task-name
|
|
4
|
+
|
|
5
|
+
import { createTask } from '@forgehive/task'
|
|
6
|
+
import { Schema } from '@forgehive/schema'
|
|
7
|
+
import fs from 'fs/promises'
|
|
8
|
+
import path from 'path'
|
|
9
|
+
import os from 'os'
|
|
10
|
+
|
|
11
|
+
import { load as loadConf } from '../conf/load'
|
|
12
|
+
import { analyzeTaskFile, TaskFingerprintOutput } from '../../utils/taskAnalysis'
|
|
13
|
+
|
|
14
|
+
interface FingerprintAnalysis {
|
|
15
|
+
taskFingerprint: TaskFingerprintOutput
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const description = 'Analyze a specific task and generate detailed fingerprint without bundling'
|
|
19
|
+
|
|
20
|
+
const schema = new Schema({
|
|
21
|
+
descriptorName: Schema.string()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const boundaries = {
|
|
25
|
+
getCwd: async (): Promise<string> => {
|
|
26
|
+
return process.cwd()
|
|
27
|
+
},
|
|
28
|
+
loadConf: loadConf.asBoundary(),
|
|
29
|
+
readFile: async (filePath: string): Promise<string> => {
|
|
30
|
+
return fs.readFile(filePath, 'utf-8')
|
|
31
|
+
},
|
|
32
|
+
writeFile: async (filePath: string, content: string): Promise<void> => {
|
|
33
|
+
return fs.writeFile(filePath, content)
|
|
34
|
+
},
|
|
35
|
+
ensureForgeFolder: async (): Promise<string> => {
|
|
36
|
+
const forgePath = path.join(os.homedir(), '.forge')
|
|
37
|
+
try {
|
|
38
|
+
await fs.access(forgePath)
|
|
39
|
+
} catch {
|
|
40
|
+
await fs.mkdir(forgePath, { recursive: true })
|
|
41
|
+
}
|
|
42
|
+
return forgePath
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const fingerprint = createTask({
|
|
47
|
+
schema,
|
|
48
|
+
boundaries,
|
|
49
|
+
fn: async function ({ descriptorName }, {
|
|
50
|
+
getCwd,
|
|
51
|
+
loadConf,
|
|
52
|
+
readFile,
|
|
53
|
+
writeFile,
|
|
54
|
+
ensureForgeFolder
|
|
55
|
+
}) {
|
|
56
|
+
const cwd = await getCwd()
|
|
57
|
+
const forgeJson = await loadConf({})
|
|
58
|
+
|
|
59
|
+
const taskDescriptor = forgeJson.tasks[descriptorName as keyof typeof forgeJson.tasks]
|
|
60
|
+
|
|
61
|
+
if (taskDescriptor === undefined) {
|
|
62
|
+
throw new Error(`Task "${descriptorName}" is not defined in forge.json`)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const filePath = path.join(cwd, taskDescriptor.path)
|
|
66
|
+
const forgePath = await ensureForgeFolder()
|
|
67
|
+
const fingerprintFile = path.join(forgePath, `${descriptorName}.task-fingerprint.json`)
|
|
68
|
+
|
|
69
|
+
console.log(`Analyzing task: ${descriptorName}`)
|
|
70
|
+
console.log(`Task file: ${filePath}`)
|
|
71
|
+
|
|
72
|
+
// Read and analyze the task file using the utility function
|
|
73
|
+
const sourceCode = await readFile(filePath)
|
|
74
|
+
const taskFingerprint = analyzeTaskFile(sourceCode, filePath)
|
|
75
|
+
|
|
76
|
+
if (!taskFingerprint) {
|
|
77
|
+
throw new Error('Could not extract fingerprint from task file: ' + filePath)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Create analysis result - clean output without extra fields
|
|
81
|
+
const analysis: FingerprintAnalysis = {
|
|
82
|
+
taskFingerprint
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Write fingerprint to file
|
|
86
|
+
await writeFile(fingerprintFile, JSON.stringify(analysis, null, 2))
|
|
87
|
+
|
|
88
|
+
console.log('Task fingerprint generated successfully')
|
|
89
|
+
console.log(`Input properties: ${Object.keys(taskFingerprint.inputSchema.properties).join(', ')}`)
|
|
90
|
+
console.log(`Boundaries: ${taskFingerprint.boundaries.join(', ')}`)
|
|
91
|
+
console.log(`Fingerprint saved to: ${fingerprintFile}`)
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
taskName: descriptorName,
|
|
95
|
+
fingerprint: taskFingerprint,
|
|
96
|
+
fingerprintFile,
|
|
97
|
+
analysis: {
|
|
98
|
+
inputSchemaProps: Object.keys(taskFingerprint.inputSchema.properties),
|
|
99
|
+
boundaryCount: taskFingerprint.boundaries.length,
|
|
100
|
+
hasDescription: !!taskFingerprint.description,
|
|
101
|
+
outputType: taskFingerprint.outputType.type
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
fingerprint.setDescription(description)
|
package/src/tasks/task/list.ts
CHANGED
|
@@ -18,10 +18,10 @@ const boundaries = {
|
|
|
18
18
|
loadConf: loadConf.asBoundary()
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export const list = createTask(
|
|
21
|
+
export const list = createTask({
|
|
22
22
|
schema,
|
|
23
23
|
boundaries,
|
|
24
|
-
async function (argv, { loadConf }) {
|
|
24
|
+
fn: async function (argv, { loadConf }) {
|
|
25
25
|
// Load forge configuration
|
|
26
26
|
const forge: ForgeConf = await loadConf({})
|
|
27
27
|
|
|
@@ -53,6 +53,6 @@ export const list = createTask(
|
|
|
53
53
|
taskCount: taskNames.length
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
)
|
|
56
|
+
})
|
|
57
57
|
|
|
58
58
|
list.setDescription(description)
|
|
@@ -81,10 +81,10 @@ const boundaries = {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
export const publish = createTask(
|
|
84
|
+
export const publish = createTask({
|
|
85
85
|
schema,
|
|
86
86
|
boundaries,
|
|
87
|
-
async function ({ descriptorName }, {
|
|
87
|
+
fn: async function ({ descriptorName }, {
|
|
88
88
|
getCwd,
|
|
89
89
|
ensureBuildsFolder,
|
|
90
90
|
loadConf,
|
|
@@ -184,4 +184,4 @@ export const publish = createTask(
|
|
|
184
184
|
throw new Error('Bundle upload failed')
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
|
-
)
|
|
187
|
+
})
|
package/src/tasks/task/remove.ts
CHANGED
|
@@ -25,10 +25,10 @@ const boundaries = {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export const remove = createTask(
|
|
28
|
+
export const remove = createTask({
|
|
29
29
|
schema,
|
|
30
30
|
boundaries,
|
|
31
|
-
async function ({ descriptorName }, { loadConf, persistConf, deleteFile }) {
|
|
31
|
+
fn: async function ({ descriptorName }, { loadConf, persistConf, deleteFile }) {
|
|
32
32
|
// Load shadow configuration
|
|
33
33
|
const forge: ForgeConf = await loadConf({})
|
|
34
34
|
|
|
@@ -61,4 +61,4 @@ export const remove = createTask(
|
|
|
61
61
|
message: `Task '${descriptorName}' has been successfully removed`
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
-
)
|
|
64
|
+
})
|
package/src/tasks/task/replay.ts
CHANGED
|
@@ -96,10 +96,10 @@ const boundaries = {
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
export const replay = createTask(
|
|
99
|
+
export const replay = createTask({
|
|
100
100
|
schema,
|
|
101
101
|
boundaries,
|
|
102
|
-
async function ({ descriptorName, path: fixturePath, cache }, { readFixture, loadConf, loadCurrentProfile, bundleCreate, bundleLoad, ensureBuildsFolder, verifyLogFolder, sendLogToAPI }) {
|
|
102
|
+
fn: async function ({ descriptorName, path: fixturePath, cache }, { readFixture, loadConf, loadCurrentProfile, bundleCreate, bundleLoad, ensureBuildsFolder, verifyLogFolder, sendLogToAPI }) {
|
|
103
103
|
console.log('Input descriptorName:', descriptorName)
|
|
104
104
|
console.log('Input path:', fixturePath)
|
|
105
105
|
console.log('Input cache:', cache)
|
|
@@ -215,6 +215,6 @@ export const replay = createTask(
|
|
|
215
215
|
|
|
216
216
|
return result
|
|
217
217
|
}
|
|
218
|
-
)
|
|
218
|
+
})
|
|
219
219
|
|
|
220
220
|
replay.setDescription(description)
|