@forgehive/forge-cli 0.1.8 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/runner.js +36 -1
- package/dist/tasks/auth/add.d.ts +16 -0
- package/dist/tasks/auth/add.js +50 -0
- package/dist/tasks/auth/list.d.ts +14 -0
- package/dist/tasks/auth/list.js +31 -0
- package/dist/tasks/auth/load.d.ts +6 -0
- package/dist/tasks/auth/load.js +44 -0
- package/dist/tasks/auth/loadCurrent.d.ts +6 -0
- package/dist/tasks/auth/loadCurrent.js +24 -0
- package/dist/tasks/auth/remove.d.ts +13 -0
- package/dist/tasks/auth/remove.js +56 -0
- package/dist/tasks/auth/switch.d.ts +12 -0
- package/dist/tasks/auth/switch.js +43 -0
- package/dist/tasks/conf/info.d.ts +7 -0
- package/dist/tasks/conf/info.js +13 -8
- package/dist/tasks/task/createTask.js +4 -0
- package/dist/tasks/task/download.d.ts +58 -0
- package/dist/tasks/task/download.js +153 -0
- package/dist/tasks/task/publish.d.ts +41 -0
- package/dist/tasks/task/publish.js +106 -0
- package/dist/tasks/types.d.ts +10 -0
- package/dist/test/tasks/create.test.js +4 -0
- package/forge.json +32 -0
- package/logs/auth:list.log +4 -0
- package/logs/auth:load.log +2 -0
- package/logs/auth:loadCurrent.log +1 -0
- package/logs/conf:info.log +1 -0
- package/package.json +5 -4
- package/src/runner.ts +37 -1
- package/src/tasks/auth/add.ts +57 -0
- package/src/tasks/auth/list.ts +43 -0
- package/src/tasks/auth/load.ts +51 -0
- package/src/tasks/auth/loadCurrent.ts +35 -0
- package/src/tasks/auth/remove.ts +66 -0
- package/src/tasks/auth/switch.ts +53 -0
- package/src/tasks/conf/info.ts +16 -8
- package/src/tasks/task/createTask.ts +4 -0
- package/src/tasks/task/download.ts +192 -0
- package/src/tasks/task/publish.ts +135 -0
- package/src/tasks/types.ts +12 -0
- package/src/test/tasks/create.test.ts +4 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// TASK: download
|
|
3
|
+
// Run this task with:
|
|
4
|
+
// forge task:run task:download --descriptorName [name] --uuid [task-uuid]
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.download = void 0;
|
|
10
|
+
const task_1 = require("@forgehive/task");
|
|
11
|
+
const schema_1 = require("@forgehive/schema");
|
|
12
|
+
const axios_1 = __importDefault(require("axios"));
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
15
|
+
const camelCase_1 = require("../../utils/camelCase");
|
|
16
|
+
const load_1 = require("../conf/load");
|
|
17
|
+
const loadCurrent_1 = require("../auth/loadCurrent");
|
|
18
|
+
const schema = new schema_1.Schema({
|
|
19
|
+
descriptorName: schema_1.Schema.string(),
|
|
20
|
+
uuid: schema_1.Schema.string()
|
|
21
|
+
});
|
|
22
|
+
const boundaries = {
|
|
23
|
+
loadCurrentProfile: loadCurrent_1.loadCurrent.asBoundary(),
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
25
|
+
downloadTask: async (uuid, profile) => {
|
|
26
|
+
const downloadUrl = `${profile.url}/api/tasks/download`;
|
|
27
|
+
console.log(`Downloading task from ${downloadUrl}...`);
|
|
28
|
+
const authToken = `${profile.apiKey}:${profile.apiSecret}`;
|
|
29
|
+
const response = await axios_1.default.post(downloadUrl, { uuid }, {
|
|
30
|
+
headers: {
|
|
31
|
+
Authorization: `Bearer ${authToken}`,
|
|
32
|
+
'Content-Type': 'application/json'
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
return response.data;
|
|
36
|
+
},
|
|
37
|
+
loadConf: load_1.load.asBoundary(),
|
|
38
|
+
getCwd: async () => {
|
|
39
|
+
return process.cwd();
|
|
40
|
+
},
|
|
41
|
+
parseTaskName: async (taskDescriptor) => {
|
|
42
|
+
const res = taskDescriptor.split(':');
|
|
43
|
+
if (res.length === 1) {
|
|
44
|
+
return {
|
|
45
|
+
descriptor: `${(0, camelCase_1.camelCase)(res[0])}`,
|
|
46
|
+
taskName: `${(0, camelCase_1.camelCase)(res[0])}`,
|
|
47
|
+
fileName: `${(0, camelCase_1.camelCase)(res[0])}.ts`
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
dir: res[0],
|
|
52
|
+
descriptor: `${res[0]}:${(0, camelCase_1.camelCase)(res[1])}`,
|
|
53
|
+
taskName: `${(0, camelCase_1.camelCase)(res[1])}`,
|
|
54
|
+
fileName: `${(0, camelCase_1.camelCase)(res[1])}.ts`
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
persistTask: async (dir, fileName, content, cwd) => {
|
|
58
|
+
const dirPath = path_1.default.resolve(cwd, dir);
|
|
59
|
+
const taskPath = path_1.default.resolve(dirPath, fileName);
|
|
60
|
+
await promises_1.default.mkdir(dirPath, { recursive: true });
|
|
61
|
+
await promises_1.default.writeFile(taskPath, content, 'utf-8');
|
|
62
|
+
return {
|
|
63
|
+
path: taskPath.toString()
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
persistConf: async (forge, cwd) => {
|
|
67
|
+
const forgePath = path_1.default.join(cwd, 'forge.json');
|
|
68
|
+
await promises_1.default.writeFile(forgePath, JSON.stringify(forge, null, 2));
|
|
69
|
+
},
|
|
70
|
+
checkTaskExists: async (dir, fileName) => {
|
|
71
|
+
const taskPath = path_1.default.resolve(dir, fileName);
|
|
72
|
+
try {
|
|
73
|
+
await promises_1.default.access(taskPath);
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
exports.download = (0, task_1.createTask)(schema, boundaries, async function ({ descriptorName, uuid }, { downloadTask, getCwd, parseTaskName, persistTask, loadConf, persistConf, checkTaskExists, loadCurrentProfile }) {
|
|
82
|
+
console.log(`Attempting to download task with descriptor: ${descriptorName} and uuid: ${uuid}`);
|
|
83
|
+
// Parse descriptor name to get task details
|
|
84
|
+
const { taskName, fileName, descriptor, dir } = await parseTaskName(descriptorName);
|
|
85
|
+
const profile = await loadCurrentProfile({});
|
|
86
|
+
const cwd = await getCwd();
|
|
87
|
+
const forge = await loadConf({});
|
|
88
|
+
let taskPath = forge.paths.tasks;
|
|
89
|
+
if (dir !== undefined) {
|
|
90
|
+
taskPath = path_1.default.join(taskPath, dir);
|
|
91
|
+
}
|
|
92
|
+
// Check if task already exists
|
|
93
|
+
const taskExists = await checkTaskExists(taskPath, fileName);
|
|
94
|
+
if (taskExists) {
|
|
95
|
+
console.log(`Task ${descriptor} already exists at ${taskPath}/${fileName}`);
|
|
96
|
+
return {
|
|
97
|
+
error: 'Task already exists',
|
|
98
|
+
taskPath: `${taskPath}/${fileName}`,
|
|
99
|
+
descriptor
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
// Download from hive api server
|
|
103
|
+
let response;
|
|
104
|
+
try {
|
|
105
|
+
response = await downloadTask(uuid, profile);
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
const error = e;
|
|
109
|
+
console.error('Error downloading task:', error.message, error.status);
|
|
110
|
+
if (error.status === 404) {
|
|
111
|
+
return {
|
|
112
|
+
error: 'Task not found',
|
|
113
|
+
taskPath: `${taskPath}/${fileName}`,
|
|
114
|
+
descriptor
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
error: 'Failed to download task',
|
|
119
|
+
message: error.message,
|
|
120
|
+
taskPath: `${taskPath}/${fileName}`,
|
|
121
|
+
descriptor
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
console.log(`
|
|
125
|
+
==================================================
|
|
126
|
+
Starting task download!
|
|
127
|
+
Creating: ${taskName}
|
|
128
|
+
Dir: ${dir ?? ''}
|
|
129
|
+
Into: ${taskPath}
|
|
130
|
+
==================================================
|
|
131
|
+
`);
|
|
132
|
+
console.log('Writing task file:', taskPath, fileName);
|
|
133
|
+
console.log('Handler:', response.handler);
|
|
134
|
+
console.log('Source code:', response.sourceCode);
|
|
135
|
+
// Persist task with cwd
|
|
136
|
+
await persistTask(taskPath, fileName, response.sourceCode, cwd);
|
|
137
|
+
// Update forge.json with the new task
|
|
138
|
+
if (forge.tasks === undefined) {
|
|
139
|
+
forge.tasks = {};
|
|
140
|
+
}
|
|
141
|
+
forge.tasks[descriptor] = {
|
|
142
|
+
path: `${taskPath}/${fileName}`,
|
|
143
|
+
handler: response.handler
|
|
144
|
+
};
|
|
145
|
+
console.log('Forge:', forge);
|
|
146
|
+
await persistConf(forge, cwd);
|
|
147
|
+
return {
|
|
148
|
+
taskPath,
|
|
149
|
+
fileName,
|
|
150
|
+
descriptor,
|
|
151
|
+
handler: response.handler
|
|
152
|
+
};
|
|
153
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Profile } from '../types';
|
|
2
|
+
export declare const publish: import("@forgehive/task").TaskInstanceType<(argv: {
|
|
3
|
+
descriptorName: string;
|
|
4
|
+
}, boundaries: import("@forgehive/task").WrappedBoundaries<{
|
|
5
|
+
getCwd: () => Promise<string>;
|
|
6
|
+
loadConf: (args: {}) => Promise<Promise<import("../types").ForgeConf>>;
|
|
7
|
+
loadCurrentProfile: (args: {}) => Promise<Promise<Profile>>;
|
|
8
|
+
bundleCreate: (args: {
|
|
9
|
+
entryPoint: string;
|
|
10
|
+
outputFile: string;
|
|
11
|
+
}) => Promise<Promise<{
|
|
12
|
+
outputFile: string;
|
|
13
|
+
}>>;
|
|
14
|
+
bundleLoad: (args: {
|
|
15
|
+
bundlePath: string;
|
|
16
|
+
}) => Promise<Promise<any>>;
|
|
17
|
+
readFileUtf8: (filePath: string) => Promise<string>;
|
|
18
|
+
readFileBinary: (filePath: string) => Promise<Buffer>;
|
|
19
|
+
publishTask: (data: any, profile: Profile) => Promise<any>;
|
|
20
|
+
ensureBuildsFolder: () => Promise<string>;
|
|
21
|
+
}>) => Promise<{
|
|
22
|
+
descriptor: import("../types").TaskDescriptor;
|
|
23
|
+
publishResponse: any;
|
|
24
|
+
}>, {
|
|
25
|
+
getCwd: () => Promise<string>;
|
|
26
|
+
loadConf: (args: {}) => Promise<Promise<import("../types").ForgeConf>>;
|
|
27
|
+
loadCurrentProfile: (args: {}) => Promise<Promise<Profile>>;
|
|
28
|
+
bundleCreate: (args: {
|
|
29
|
+
entryPoint: string;
|
|
30
|
+
outputFile: string;
|
|
31
|
+
}) => Promise<Promise<{
|
|
32
|
+
outputFile: string;
|
|
33
|
+
}>>;
|
|
34
|
+
bundleLoad: (args: {
|
|
35
|
+
bundlePath: string;
|
|
36
|
+
}) => Promise<Promise<any>>;
|
|
37
|
+
readFileUtf8: (filePath: string) => Promise<string>;
|
|
38
|
+
readFileBinary: (filePath: string) => Promise<Buffer>;
|
|
39
|
+
publishTask: (data: any, profile: Profile) => Promise<any>;
|
|
40
|
+
ensureBuildsFolder: () => Promise<string>;
|
|
41
|
+
}>;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// TASK: publish
|
|
3
|
+
// Run this task with:
|
|
4
|
+
// forge task:run task:publish --descriptorMame task-name
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.publish = void 0;
|
|
10
|
+
const task_1 = require("@forgehive/task");
|
|
11
|
+
const schema_1 = require("@forgehive/schema");
|
|
12
|
+
const axios_1 = __importDefault(require("axios"));
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
15
|
+
const os_1 = __importDefault(require("os"));
|
|
16
|
+
const load_1 = require("../conf/load");
|
|
17
|
+
const create_1 = require("../bundle/create");
|
|
18
|
+
const load_2 = require("../bundle/load");
|
|
19
|
+
const loadCurrent_1 = require("../auth/loadCurrent");
|
|
20
|
+
const schema = new schema_1.Schema({
|
|
21
|
+
descriptorName: schema_1.Schema.string()
|
|
22
|
+
});
|
|
23
|
+
const boundaries = {
|
|
24
|
+
getCwd: async () => {
|
|
25
|
+
return process.cwd();
|
|
26
|
+
},
|
|
27
|
+
loadConf: load_1.load.asBoundary(),
|
|
28
|
+
loadCurrentProfile: loadCurrent_1.loadCurrent.asBoundary(),
|
|
29
|
+
bundleCreate: create_1.create.asBoundary(),
|
|
30
|
+
bundleLoad: load_2.load.asBoundary(),
|
|
31
|
+
readFileUtf8: async (filePath) => {
|
|
32
|
+
return promises_1.default.readFile(filePath, 'utf-8');
|
|
33
|
+
},
|
|
34
|
+
readFileBinary: async (filePath) => {
|
|
35
|
+
return promises_1.default.readFile(filePath);
|
|
36
|
+
},
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
38
|
+
publishTask: async (data, profile) => {
|
|
39
|
+
const publishUrl = `${profile.url}/api/tasks/publish`;
|
|
40
|
+
console.log(`Publishing task to ${publishUrl}...`);
|
|
41
|
+
const authToken = `${profile.apiKey}:${profile.apiSecret}`;
|
|
42
|
+
const response = await axios_1.default.post(publishUrl, data, {
|
|
43
|
+
headers: {
|
|
44
|
+
Authorization: `Bearer ${authToken}`,
|
|
45
|
+
'Content-Type': 'application/json'
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
return response.data;
|
|
49
|
+
},
|
|
50
|
+
ensureBuildsFolder: async () => {
|
|
51
|
+
const buildsPath = path_1.default.join(os_1.default.homedir(), '.forge', 'builds');
|
|
52
|
+
try {
|
|
53
|
+
await promises_1.default.access(buildsPath);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
await promises_1.default.mkdir(buildsPath, { recursive: true });
|
|
57
|
+
}
|
|
58
|
+
return buildsPath;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
exports.publish = (0, task_1.createTask)(schema, boundaries, async function ({ descriptorName }, { getCwd, ensureBuildsFolder, loadConf, bundleCreate, bundleLoad, readFileUtf8, readFileBinary, publishTask, loadCurrentProfile }) {
|
|
62
|
+
const cwd = await getCwd();
|
|
63
|
+
const forgeJson = await loadConf({});
|
|
64
|
+
const profile = await loadCurrentProfile({});
|
|
65
|
+
const taskDescriptor = forgeJson.tasks[descriptorName];
|
|
66
|
+
const projectName = forgeJson.project.name;
|
|
67
|
+
if (taskDescriptor === undefined) {
|
|
68
|
+
throw new Error('Task is not defined on forge.json');
|
|
69
|
+
}
|
|
70
|
+
const entryPoint = path_1.default.join(cwd, taskDescriptor.path);
|
|
71
|
+
const buildsPath = await ensureBuildsFolder();
|
|
72
|
+
const outputFile = path_1.default.join(buildsPath, `${descriptorName}.js`);
|
|
73
|
+
console.log('entryPoint:', entryPoint);
|
|
74
|
+
console.log('buildsPath:', buildsPath);
|
|
75
|
+
console.log('outputFile:', outputFile);
|
|
76
|
+
// Bundle the task
|
|
77
|
+
await bundleCreate({
|
|
78
|
+
entryPoint,
|
|
79
|
+
outputFile
|
|
80
|
+
});
|
|
81
|
+
// Load the bundled task
|
|
82
|
+
const bundle = await bundleLoad({
|
|
83
|
+
bundlePath: outputFile
|
|
84
|
+
});
|
|
85
|
+
// Get the task handler
|
|
86
|
+
const task = bundle[taskDescriptor.handler];
|
|
87
|
+
const description = task.getDescription() ?? '';
|
|
88
|
+
const schema = task.getSchema() || new schema_1.Schema({});
|
|
89
|
+
const schemaDescriptor = schema.describe();
|
|
90
|
+
// Read the task file content and bundle
|
|
91
|
+
const sourceCode = await readFileUtf8(entryPoint);
|
|
92
|
+
const bundleContent = await readFileBinary(outputFile);
|
|
93
|
+
const data = {
|
|
94
|
+
...taskDescriptor,
|
|
95
|
+
taskName: descriptorName,
|
|
96
|
+
projectName,
|
|
97
|
+
description,
|
|
98
|
+
schemaDescriptor: JSON.stringify(schemaDescriptor),
|
|
99
|
+
sourceCode,
|
|
100
|
+
bundle: bundleContent.toString('base64')
|
|
101
|
+
};
|
|
102
|
+
// Publish to hive api server
|
|
103
|
+
const response = await publishTask(data, profile);
|
|
104
|
+
console.log('Publish response:', response);
|
|
105
|
+
return { descriptor: taskDescriptor, publishResponse: response };
|
|
106
|
+
});
|
package/dist/tasks/types.d.ts
CHANGED
|
@@ -35,3 +35,13 @@ export interface TaskName {
|
|
|
35
35
|
fileName: string;
|
|
36
36
|
dir?: string;
|
|
37
37
|
}
|
|
38
|
+
export interface Profile {
|
|
39
|
+
name: string;
|
|
40
|
+
apiKey: string;
|
|
41
|
+
apiSecret: string;
|
|
42
|
+
url: string;
|
|
43
|
+
}
|
|
44
|
+
export interface Profiles {
|
|
45
|
+
default: string;
|
|
46
|
+
profiles: Profile[];
|
|
47
|
+
}
|
|
@@ -15,6 +15,8 @@ const expectedContent = `// TASK: newTask
|
|
|
15
15
|
import { createTask } from '@forgehive/task'
|
|
16
16
|
import { Schema } from '@forgehive/schema'
|
|
17
17
|
|
|
18
|
+
const description = 'Add task description here'
|
|
19
|
+
|
|
18
20
|
const schema = new Schema({
|
|
19
21
|
// Add your schema definitions here
|
|
20
22
|
// example: myParam: Schema.string()
|
|
@@ -37,6 +39,8 @@ export const newTask = createTask(
|
|
|
37
39
|
return status
|
|
38
40
|
}
|
|
39
41
|
)
|
|
42
|
+
|
|
43
|
+
newTask.setDescription(description)
|
|
40
44
|
`;
|
|
41
45
|
describe('Create task', () => {
|
|
42
46
|
let volume;
|
package/forge.json
CHANGED
|
@@ -45,6 +45,38 @@
|
|
|
45
45
|
"runner:bundle": {
|
|
46
46
|
"path": "src/tasks/runner/bundle.ts",
|
|
47
47
|
"handler": "bundle"
|
|
48
|
+
},
|
|
49
|
+
"task:publish": {
|
|
50
|
+
"path": "src/tasks/task/publish.ts",
|
|
51
|
+
"handler": "publish"
|
|
52
|
+
},
|
|
53
|
+
"task:download": {
|
|
54
|
+
"path": "src/tasks/task/download.ts",
|
|
55
|
+
"handler": "download"
|
|
56
|
+
},
|
|
57
|
+
"auth:add": {
|
|
58
|
+
"path": "src/tasks/auth/add.ts",
|
|
59
|
+
"handler": "add"
|
|
60
|
+
},
|
|
61
|
+
"auth:load": {
|
|
62
|
+
"path": "src/tasks/auth/load.ts",
|
|
63
|
+
"handler": "load"
|
|
64
|
+
},
|
|
65
|
+
"auth:loadCurrent": {
|
|
66
|
+
"path": "src/tasks/auth/loadCurrent.ts",
|
|
67
|
+
"handler": "loadCurrent"
|
|
68
|
+
},
|
|
69
|
+
"auth:switch": {
|
|
70
|
+
"path": "src/tasks/auth/switch.ts",
|
|
71
|
+
"handler": "switchProfile"
|
|
72
|
+
},
|
|
73
|
+
"auth:list": {
|
|
74
|
+
"path": "src/tasks/auth/list.ts",
|
|
75
|
+
"handler": "list"
|
|
76
|
+
},
|
|
77
|
+
"auth:remove": {
|
|
78
|
+
"path": "src/tasks/auth/remove.ts",
|
|
79
|
+
"handler": "remove"
|
|
48
80
|
}
|
|
49
81
|
},
|
|
50
82
|
"runners": {}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{"name":"auth:list","type":"success","input":{},"output":{"status":"Ok","profiles":[{"name":"local","apiKey":"b4b5a766fcd7dc2d059e8f96a57c8edd","apiSecret":"2900246cb8bebcbeaadbe6348477592f42d62788d13fd4067588438bc11bf116","url":"http://localhost:4000"},{"name":"localNext","apiKey":"b4b5a766fcd7dc2d059e8f96a57c8edd","apiSecret":"2900246cb8bebcbeaadbe6348477592f42d62788d13fd4067588438bc11bf116","url":"http://localhost:3000"}],"default":"localNext"},"boundaries":{"loadProfiles":[{"input":[{}],"output":{"default":"localNext","profiles":[{"name":"local","apiKey":"b4b5a766fcd7dc2d059e8f96a57c8edd","apiSecret":"2900246cb8bebcbeaadbe6348477592f42d62788d13fd4067588438bc11bf116","url":"http://localhost:4000"},{"name":"localNext","apiKey":"b4b5a766fcd7dc2d059e8f96a57c8edd","apiSecret":"2900246cb8bebcbeaadbe6348477592f42d62788d13fd4067588438bc11bf116","url":"http://localhost:3000"}]}},{"input":[{}],"output":{"default":"localNext","profiles":[{"name":"local","apiKey":"b4b5a766fcd7dc2d059e8f96a57c8edd","apiSecret":"2900246cb8bebcbeaadbe6348477592f42d62788d13fd4067588438bc11bf116","url":"http://localhost:4000"},{"name":"localNext","apiKey":"b4b5a766fcd7dc2d059e8f96a57c8edd","apiSecret":"2900246cb8bebcbeaadbe6348477592f42d62788d13fd4067588438bc11bf116","url":"http://localhost:3000"}]}}]}}
|
|
2
|
+
{"name":"auth:list","type":"success","input":{},"output":{"status":"Ok","default":"localNext"},"boundaries":{"loadProfiles":[{"input":[{}],"output":{"default":"localNext","profiles":[{"name":"local","apiKey":"b4b5a766fcd7dc2d059e8f96a57c8edd","apiSecret":"2900246cb8bebcbeaadbe6348477592f42d62788d13fd4067588438bc11bf116","url":"http://localhost:4000"},{"name":"localNext","apiKey":"b4b5a766fcd7dc2d059e8f96a57c8edd","apiSecret":"2900246cb8bebcbeaadbe6348477592f42d62788d13fd4067588438bc11bf116","url":"http://localhost:3000"}]}}]}}
|
|
3
|
+
{"name":"auth:list","type":"success","input":{},"output":{"default":"localNext"},"boundaries":{"loadProfiles":[{"input":[{}],"output":{"default":"localNext","profiles":[{"name":"local","apiKey":"b4b5a766fcd7dc2d059e8f96a57c8edd","apiSecret":"2900246cb8bebcbeaadbe6348477592f42d62788d13fd4067588438bc11bf116","url":"http://localhost:4000"},{"name":"localNext","apiKey":"b4b5a766fcd7dc2d059e8f96a57c8edd","apiSecret":"2900246cb8bebcbeaadbe6348477592f42d62788d13fd4067588438bc11bf116","url":"http://localhost:3000"}]}}]}}
|
|
4
|
+
{"name":"auth:list","type":"success","input":{},"output":{"default":"localNext"},"boundaries":{"loadProfiles":[{"input":[{}],"output":{"default":"localNext","profiles":[{"name":"local","apiKey":"b4b5a766fcd7dc2d059e8f96a57c8edd","apiSecret":"2900246cb8bebcbeaadbe6348477592f42d62788d13fd4067588438bc11bf116","url":"http://localhost:4000"},{"name":"localNext","apiKey":"b4b5a766fcd7dc2d059e8f96a57c8edd","apiSecret":"2900246cb8bebcbeaadbe6348477592f42d62788d13fd4067588438bc11bf116","url":"http://localhost:3000"}]}}]}}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
{"name":"auth:load","type":"error","input":{},"error":"ENOENT: no such file or directory, open '/Users/danielzavaladlvega/.forge/profiles.json'","boundaries":{"ensureBuildsFolder":[{"input":[],"output":"/Users/danielzavaladlvega/.forge"},{"input":[],"output":"/Users/danielzavaladlvega/.forge"}]}}
|
|
2
|
+
{"name":"auth:load","type":"success","input":{},"output":{"profiles":[]},"boundaries":{"ensureBuildsFolder":[{"input":[],"output":"/Users/danielzavaladlvega/.forge"}]}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name":"auth:loadCurrent","type":"success","input":{},"output":{"name":"local","apiKey":"b4b5a766fcd7dc2d059e8f96a57c8edd","apiSecret":"2900246cb8bebcbeaadbe6348477592f42d62788d13fd4067588438bc11bf116","url":"http://localhost:4000"},"boundaries":{"loadProfiles":[{"input":[{}],"output":{"default":"local","profiles":[{"name":"local","apiKey":"b4b5a766fcd7dc2d059e8f96a57c8edd","apiSecret":"2900246cb8bebcbeaadbe6348477592f42d62788d13fd4067588438bc11bf116","url":"http://localhost:4000"}]}}]}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name":"conf:info","type":"error","input":{},"error":"ENOENT: no such file or directory, open '/Users/package.json'","boundaries":{"readFile":[{"input":["/Users/package.json"],"error":"ENOENT: no such file or directory, open '/Users/package.json'"}],"loadCurrentProfile":[]}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgehive/forge-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "TypeScript CLI application",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -20,13 +20,14 @@
|
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
+
"axios": "^1.8.4",
|
|
23
24
|
"esbuild": "^0.25.0",
|
|
24
25
|
"handlebars": "^4.7.8",
|
|
25
26
|
"minimist": "^1.2.8",
|
|
26
|
-
"@forgehive/task": "0.1.4",
|
|
27
27
|
"@forgehive/schema": "0.1.4",
|
|
28
|
-
"@forgehive/
|
|
29
|
-
"@forgehive/record-tape": "0.0.1"
|
|
28
|
+
"@forgehive/task": "0.1.4",
|
|
29
|
+
"@forgehive/record-tape": "0.0.1",
|
|
30
|
+
"@forgehive/runner": "0.1.4"
|
|
30
31
|
},
|
|
31
32
|
"devDependencies": {
|
|
32
33
|
"@types/jest": "^29.5.3",
|
package/src/runner.ts
CHANGED
|
@@ -11,6 +11,13 @@ import { remove as taskRemoveCommand } from './tasks/task/remove'
|
|
|
11
11
|
import { create as createRunner } from './tasks/runner/create'
|
|
12
12
|
import { remove as removeRunner } from './tasks/runner/remove'
|
|
13
13
|
import { bundle as bundleRunner } from './tasks/runner/bundle'
|
|
14
|
+
import { publish as publishTask } from './tasks/task/publish'
|
|
15
|
+
import { download as downloadTask } from './tasks/task/download'
|
|
16
|
+
|
|
17
|
+
import { add as addProfile } from './tasks/auth/add'
|
|
18
|
+
import { switchProfile } from './tasks/auth/switch'
|
|
19
|
+
import { list as listProfiles } from './tasks/auth/list'
|
|
20
|
+
import { remove as removeProfile } from './tasks/auth/remove'
|
|
14
21
|
|
|
15
22
|
interface CliParsedArguments extends RunnerParsedArguments {
|
|
16
23
|
action: string;
|
|
@@ -34,12 +41,20 @@ runner.load('info', info)
|
|
|
34
41
|
runner.load('task:create', createTaskCommand)
|
|
35
42
|
runner.load('task:run', taskRunCommand)
|
|
36
43
|
runner.load('task:remove', taskRemoveCommand)
|
|
44
|
+
runner.load('task:publish', publishTask)
|
|
45
|
+
runner.load('task:download', downloadTask)
|
|
37
46
|
|
|
38
47
|
// Runner commands
|
|
39
48
|
runner.load('runner:create', createRunner)
|
|
40
49
|
runner.load('runner:remove', removeRunner)
|
|
41
50
|
runner.load('runner:bundle', bundleRunner)
|
|
42
51
|
|
|
52
|
+
// Auth commands
|
|
53
|
+
runner.load('auth:add', addProfile)
|
|
54
|
+
runner.load('auth:switch', switchProfile)
|
|
55
|
+
runner.load('auth:list', listProfiles)
|
|
56
|
+
runner.load('auth:remove', removeProfile)
|
|
57
|
+
|
|
43
58
|
// Set handler
|
|
44
59
|
runner.setHandler(async (data: ParsedArgs): Promise<unknown> => {
|
|
45
60
|
const parsedArgs = runner.parseArguments(data)
|
|
@@ -57,10 +72,11 @@ runner.setHandler(async (data: ParsedArgs): Promise<unknown> => {
|
|
|
57
72
|
try {
|
|
58
73
|
let result
|
|
59
74
|
|
|
60
|
-
const commandsWithDescriptor = ['task:create', 'task:remove']
|
|
75
|
+
const commandsWithDescriptor = ['task:create', 'task:remove', 'task:publish']
|
|
61
76
|
const commandsWithRunner = ['runner:create', 'runner:remove']
|
|
62
77
|
|
|
63
78
|
if (commandsWithDescriptor.includes(taskName)) {
|
|
79
|
+
console.log('Running:', taskName, action)
|
|
64
80
|
result = await task.run({ descriptorName: action })
|
|
65
81
|
} else if (commandsWithRunner.includes(taskName)) {
|
|
66
82
|
result = await task.run({
|
|
@@ -73,11 +89,31 @@ runner.setHandler(async (data: ParsedArgs): Promise<unknown> => {
|
|
|
73
89
|
runnerName: action,
|
|
74
90
|
targetPath: paths.targetPath
|
|
75
91
|
})
|
|
92
|
+
} else if (taskName === 'task:download') {
|
|
93
|
+
const { uuid } = args as { uuid: string }
|
|
94
|
+
|
|
95
|
+
result = await task.run({
|
|
96
|
+
descriptorName: action,
|
|
97
|
+
uuid
|
|
98
|
+
})
|
|
76
99
|
} else if (taskName === 'task:run') {
|
|
77
100
|
result = await task.run({
|
|
78
101
|
descriptorName: action,
|
|
79
102
|
args
|
|
80
103
|
})
|
|
104
|
+
} else if (taskName === 'auth:add') {
|
|
105
|
+
const { apiKey, apiSecret, url } = args as { name: string, apiKey: string, apiSecret: string, url: string }
|
|
106
|
+
|
|
107
|
+
result = await task.run({
|
|
108
|
+
name: action,
|
|
109
|
+
apiKey,
|
|
110
|
+
apiSecret,
|
|
111
|
+
url
|
|
112
|
+
})
|
|
113
|
+
} else if (taskName === 'auth:switch' || taskName === 'auth:remove') {
|
|
114
|
+
result = await task.run({
|
|
115
|
+
profileName: action
|
|
116
|
+
})
|
|
81
117
|
} else {
|
|
82
118
|
result = await task.run(args)
|
|
83
119
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// TASK: add
|
|
2
|
+
// Run this task with:
|
|
3
|
+
// forge task:run auth:add
|
|
4
|
+
|
|
5
|
+
import { createTask } from '@forgehive/task'
|
|
6
|
+
import { Schema } from '@forgehive/schema'
|
|
7
|
+
import path from 'path'
|
|
8
|
+
import fs from 'fs/promises'
|
|
9
|
+
import os from 'os'
|
|
10
|
+
|
|
11
|
+
import { load as loadProfiles } from './load'
|
|
12
|
+
import { type Profiles } from '../types'
|
|
13
|
+
|
|
14
|
+
const schema = new Schema({
|
|
15
|
+
name: Schema.string(),
|
|
16
|
+
apiKey: Schema.string(),
|
|
17
|
+
apiSecret: Schema.string(),
|
|
18
|
+
url: Schema.string()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const boundaries = {
|
|
22
|
+
loadProfiles: loadProfiles.asBoundary(),
|
|
23
|
+
persistProfiles: async (profiles: Profiles): Promise<void> => {
|
|
24
|
+
const buildsPath = path.join(os.homedir(), '.forge')
|
|
25
|
+
const profilesPath = path.join(buildsPath, 'profiles.json')
|
|
26
|
+
await fs.writeFile(profilesPath, JSON.stringify(profiles, null, 2))
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const add = createTask(
|
|
31
|
+
schema,
|
|
32
|
+
boundaries,
|
|
33
|
+
async function ({ name, apiKey, apiSecret, url }, { loadProfiles, persistProfiles }) {
|
|
34
|
+
const profiles = await loadProfiles({})
|
|
35
|
+
|
|
36
|
+
// Check if profile with same name already exists
|
|
37
|
+
const existingProfileIndex = profiles.profiles.findIndex(p => p.name === name)
|
|
38
|
+
if (existingProfileIndex >= 0) {
|
|
39
|
+
// Replace existing profile
|
|
40
|
+
profiles.profiles[existingProfileIndex] = { name, apiKey, apiSecret, url }
|
|
41
|
+
} else {
|
|
42
|
+
// Add new profile
|
|
43
|
+
profiles.profiles.push({ name, apiKey, apiSecret, url })
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Set as default profile
|
|
47
|
+
profiles.default = name
|
|
48
|
+
|
|
49
|
+
// Persist profiles
|
|
50
|
+
await persistProfiles(profiles)
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
status: 'Ok',
|
|
54
|
+
message: `Profile '${name}' added and set as default`
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// TASK: list
|
|
2
|
+
// Run this task with:
|
|
3
|
+
// forge task:run auth:list
|
|
4
|
+
|
|
5
|
+
import { createTask } from '@forgehive/task'
|
|
6
|
+
import { Schema } from '@forgehive/schema'
|
|
7
|
+
|
|
8
|
+
import { load as loadProfiles } from './load'
|
|
9
|
+
import { type Profiles } from '../types'
|
|
10
|
+
|
|
11
|
+
const schema = new Schema({})
|
|
12
|
+
|
|
13
|
+
const boundaries = {
|
|
14
|
+
loadProfiles: loadProfiles.asBoundary()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const list = createTask(
|
|
18
|
+
schema,
|
|
19
|
+
boundaries,
|
|
20
|
+
async function (_argv, { loadProfiles }) {
|
|
21
|
+
const profiles: Profiles = await loadProfiles({})
|
|
22
|
+
|
|
23
|
+
if (profiles.profiles.length === 0) {
|
|
24
|
+
console.log('No profiles found. Use auth:add to create one.')
|
|
25
|
+
return { status: 'Ok', profiles: [] }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
console.log('Available profiles:')
|
|
29
|
+
|
|
30
|
+
profiles.profiles.forEach(profile => {
|
|
31
|
+
const isDefault = profile.name === profiles.default
|
|
32
|
+
const prefix = isDefault ? '* ' : ' '
|
|
33
|
+
console.log(`${prefix}${profile.name} - API Key: ${profile.apiKey}`)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
console.log('\nUse auth:add to create or update a profile')
|
|
37
|
+
console.log('\nUse auth:switch to switch to a profile')
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
default: profiles.default
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// TASK: load
|
|
2
|
+
// Run this task with:
|
|
3
|
+
// forge task:run auth:load
|
|
4
|
+
|
|
5
|
+
import os from 'os'
|
|
6
|
+
import path from 'path'
|
|
7
|
+
import fs from 'fs/promises'
|
|
8
|
+
|
|
9
|
+
import { createTask } from '@forgehive/task'
|
|
10
|
+
import { Schema } from '@forgehive/schema'
|
|
11
|
+
|
|
12
|
+
import { type Profiles } from '../types'
|
|
13
|
+
|
|
14
|
+
const schema = new Schema({})
|
|
15
|
+
|
|
16
|
+
const boundaries = {
|
|
17
|
+
ensureBuildsFolder: async (): Promise<string> => {
|
|
18
|
+
const buildsPath = path.join(os.homedir(), '.forge')
|
|
19
|
+
try {
|
|
20
|
+
await fs.access(buildsPath)
|
|
21
|
+
} catch {
|
|
22
|
+
await fs.mkdir(buildsPath, { recursive: true })
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return buildsPath
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const load = createTask(
|
|
30
|
+
schema,
|
|
31
|
+
boundaries,
|
|
32
|
+
async function (argv, { ensureBuildsFolder }) {
|
|
33
|
+
const buildsPath = await ensureBuildsFolder()
|
|
34
|
+
|
|
35
|
+
let profiles: Profiles = {
|
|
36
|
+
default: '',
|
|
37
|
+
profiles: []
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const profilesPath = path.join(buildsPath, 'profiles.json')
|
|
41
|
+
try {
|
|
42
|
+
const content = await fs.readFile(profilesPath, 'utf-8')
|
|
43
|
+
profiles = JSON.parse(content) as Profiles
|
|
44
|
+
} catch (_error) {
|
|
45
|
+
console.log('Creating profiles.json')
|
|
46
|
+
await fs.writeFile(profilesPath, '{"profiles": [], "default": ""}')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return profiles
|
|
50
|
+
}
|
|
51
|
+
)
|