@forgehive/forge-cli 0.3.11 → 0.3.13
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 +9 -6
- package/dist/tasks/auth/add.d.ts +16 -0
- package/dist/tasks/auth/add.js +56 -4
- package/dist/tasks/auth/clear.d.ts +16 -0
- package/dist/tasks/auth/clear.js +54 -0
- package/dist/tasks/auth/list.d.ts +2 -0
- package/dist/tasks/auth/list.js +10 -2
- package/dist/tasks/project/sync.d.ts +116 -0
- package/dist/tasks/project/sync.js +152 -0
- package/dist/tasks/task/createTask.d.ts +12 -0
- package/dist/tasks/task/createTask.js +51 -2
- package/dist/tasks/task/run.d.ts +12 -2
- package/dist/tasks/task/run.js +39 -10
- package/dist/tasks/types.d.ts +3 -0
- package/logs/task:list.log +1 -0
- package/logs/test:guidance.log +1 -0
- package/logs/test:uuid.log +1 -0
- package/logs/test:uuidCheck.log +1 -0
- package/package.json +10 -10
- package/src/runner.ts +9 -6
- package/src/tasks/auth/add.ts +66 -4
- package/src/tasks/auth/clear.ts +63 -0
- package/src/tasks/auth/list.ts +10 -2
- package/src/tasks/project/sync.ts +202 -0
- package/src/tasks/task/createTask.ts +68 -2
- package/src/tasks/task/run.ts +49 -10
- package/src/tasks/types.ts +3 -0
package/dist/runner.js
CHANGED
|
@@ -29,9 +29,11 @@ const add_1 = require("./tasks/auth/add");
|
|
|
29
29
|
const switch_1 = require("./tasks/auth/switch");
|
|
30
30
|
const list_2 = require("./tasks/auth/list");
|
|
31
31
|
const remove_3 = require("./tasks/auth/remove");
|
|
32
|
+
const clear_1 = require("./tasks/auth/clear");
|
|
32
33
|
const create_2 = require("./tasks/project/create");
|
|
33
34
|
const link_1 = require("./tasks/project/link");
|
|
34
35
|
const unlink_1 = require("./tasks/project/unlink");
|
|
36
|
+
const sync_1 = require("./tasks/project/sync");
|
|
35
37
|
const runner = new runner_1.Runner((data) => {
|
|
36
38
|
const { _, ...filteredObj } = data;
|
|
37
39
|
return {
|
|
@@ -67,10 +69,12 @@ runner.load('auth:add', add_1.add);
|
|
|
67
69
|
runner.load('auth:switch', switch_1.switchProfile);
|
|
68
70
|
runner.load('auth:list', list_2.list);
|
|
69
71
|
runner.load('auth:remove', remove_3.remove);
|
|
72
|
+
runner.load('auth:clear', clear_1.clear);
|
|
70
73
|
// Project commands
|
|
71
74
|
runner.load('project:create', create_2.create);
|
|
72
75
|
runner.load('project:link', link_1.link);
|
|
73
76
|
runner.load('project:unlink', unlink_1.unlink);
|
|
77
|
+
runner.load('project:sync', sync_1.sync);
|
|
74
78
|
// Set handler
|
|
75
79
|
runner.setHandler(async (data) => {
|
|
76
80
|
const parsedArgs = runner.parseArguments(data);
|
|
@@ -81,12 +85,14 @@ runner.setHandler(async (data) => {
|
|
|
81
85
|
let silent = false;
|
|
82
86
|
const task = runner.getTask(taskName);
|
|
83
87
|
if (!task) {
|
|
84
|
-
throw new Error(`
|
|
88
|
+
throw new Error(`Forge command "${taskName}" not found`);
|
|
85
89
|
}
|
|
86
90
|
try {
|
|
87
91
|
let result;
|
|
88
92
|
const commandsWithDescriptor = ['task:create', 'task:remove', 'task:publish', 'task:describe', 'task:fingerprint'];
|
|
89
93
|
const commandsWithRunner = ['runner:create', 'runner:remove'];
|
|
94
|
+
const commandsWithoutParams = ['project:unlink', 'project:sync', 'auth:clear'];
|
|
95
|
+
const silentCommands = ['task:describe', 'task:list', 'auth:list', 'info'];
|
|
90
96
|
if (commandsWithDescriptor.includes(taskName)) {
|
|
91
97
|
result = await task.run({ descriptorName: action });
|
|
92
98
|
}
|
|
@@ -175,16 +181,13 @@ runner.setHandler(async (data) => {
|
|
|
175
181
|
uuid
|
|
176
182
|
});
|
|
177
183
|
}
|
|
178
|
-
else if (taskName
|
|
184
|
+
else if (commandsWithoutParams.includes(taskName)) {
|
|
179
185
|
result = await task.run({});
|
|
180
186
|
}
|
|
181
187
|
else {
|
|
182
188
|
result = await task.run(args);
|
|
183
|
-
if (taskName === 'info') {
|
|
184
|
-
silent = true;
|
|
185
|
-
}
|
|
186
189
|
}
|
|
187
|
-
if (taskName
|
|
190
|
+
if (silentCommands.includes(taskName)) {
|
|
188
191
|
silent = true;
|
|
189
192
|
}
|
|
190
193
|
return {
|
package/dist/tasks/auth/add.d.ts
CHANGED
|
@@ -7,10 +7,26 @@ export declare const add: import("@forgehive/task").TaskInstanceType<(argv: {
|
|
|
7
7
|
}, boundaries: import("@forgehive/task").WrappedBoundaries<{
|
|
8
8
|
loadProfiles: (args: {}) => Promise<Promise<Profiles>>;
|
|
9
9
|
persistProfiles: (profiles: Profiles) => Promise<void>;
|
|
10
|
+
fetchMeInfo: (apiKey: string, apiSecret: string, url: string) => Promise<{
|
|
11
|
+
success: boolean;
|
|
12
|
+
teamName?: string;
|
|
13
|
+
teamUuid?: string;
|
|
14
|
+
userName?: string;
|
|
15
|
+
error?: string;
|
|
16
|
+
}>;
|
|
10
17
|
}>) => Promise<{
|
|
11
18
|
status: string;
|
|
12
19
|
message: string;
|
|
20
|
+
teamName: string | undefined;
|
|
21
|
+
userName: string | undefined;
|
|
13
22
|
}>, {
|
|
14
23
|
loadProfiles: (args: {}) => Promise<Promise<Profiles>>;
|
|
15
24
|
persistProfiles: (profiles: Profiles) => Promise<void>;
|
|
25
|
+
fetchMeInfo: (apiKey: string, apiSecret: string, url: string) => Promise<{
|
|
26
|
+
success: boolean;
|
|
27
|
+
teamName?: string;
|
|
28
|
+
teamUuid?: string;
|
|
29
|
+
userName?: string;
|
|
30
|
+
error?: string;
|
|
31
|
+
}>;
|
|
16
32
|
}>;
|
package/dist/tasks/auth/add.js
CHANGED
|
@@ -25,22 +25,72 @@ const boundaries = {
|
|
|
25
25
|
const buildsPath = path_1.default.join(os_1.default.homedir(), '.forge');
|
|
26
26
|
const profilesPath = path_1.default.join(buildsPath, 'profiles.json');
|
|
27
27
|
await promises_1.default.writeFile(profilesPath, JSON.stringify(profiles, null, 2));
|
|
28
|
+
},
|
|
29
|
+
fetchMeInfo: async (apiKey, apiSecret, url) => {
|
|
30
|
+
try {
|
|
31
|
+
const response = await fetch(`${url}/api/me`, {
|
|
32
|
+
method: 'GET',
|
|
33
|
+
headers: {
|
|
34
|
+
'Authorization': `Bearer ${apiKey}:${apiSecret}`,
|
|
35
|
+
'Content-Type': 'application/json'
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
if (response.ok) {
|
|
39
|
+
const data = await response.json();
|
|
40
|
+
return {
|
|
41
|
+
success: true,
|
|
42
|
+
teamName: data.team?.name,
|
|
43
|
+
teamUuid: data.team?.uuid,
|
|
44
|
+
userName: data.user?.name
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
49
|
+
return { success: false, error: errorData.error || `HTTP ${response.status}` };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
return { success: false, error: error instanceof Error ? error.message : 'Network error' };
|
|
54
|
+
}
|
|
28
55
|
}
|
|
29
56
|
};
|
|
30
57
|
exports.add = (0, task_1.createTask)({
|
|
31
58
|
schema,
|
|
32
59
|
boundaries,
|
|
33
|
-
fn: async function ({ name, apiKey, apiSecret, url }, { loadProfiles, persistProfiles }) {
|
|
60
|
+
fn: async function ({ name, apiKey, apiSecret, url }, { loadProfiles, persistProfiles, fetchMeInfo }) {
|
|
34
61
|
const profiles = await loadProfiles({});
|
|
62
|
+
console.log('Verifying credentials...');
|
|
63
|
+
// Fetch team and user information from /me endpoint
|
|
64
|
+
const meInfo = await fetchMeInfo(apiKey, apiSecret, url);
|
|
65
|
+
if (!meInfo.success) {
|
|
66
|
+
throw new Error(`Failed to verify credentials: ${meInfo.error}`);
|
|
67
|
+
}
|
|
68
|
+
console.log('✅ Credentials verified');
|
|
69
|
+
if (meInfo.userName) {
|
|
70
|
+
console.log(` User: ${meInfo.userName}`);
|
|
71
|
+
}
|
|
72
|
+
if (meInfo.teamName) {
|
|
73
|
+
console.log(` Team: ${meInfo.teamName}`);
|
|
74
|
+
}
|
|
75
|
+
// Create profile with team information
|
|
76
|
+
const profile = {
|
|
77
|
+
name,
|
|
78
|
+
apiKey,
|
|
79
|
+
apiSecret,
|
|
80
|
+
url,
|
|
81
|
+
teamName: meInfo.teamName,
|
|
82
|
+
teamUuid: meInfo.teamUuid,
|
|
83
|
+
userName: meInfo.userName
|
|
84
|
+
};
|
|
35
85
|
// Check if profile with same name already exists
|
|
36
86
|
const existingProfileIndex = profiles.profiles.findIndex(p => p.name === name);
|
|
37
87
|
if (existingProfileIndex >= 0) {
|
|
38
88
|
// Replace existing profile
|
|
39
|
-
profiles.profiles[existingProfileIndex] =
|
|
89
|
+
profiles.profiles[existingProfileIndex] = profile;
|
|
40
90
|
}
|
|
41
91
|
else {
|
|
42
92
|
// Add new profile
|
|
43
|
-
profiles.profiles.push(
|
|
93
|
+
profiles.profiles.push(profile);
|
|
44
94
|
}
|
|
45
95
|
// Set as default profile
|
|
46
96
|
profiles.default = name;
|
|
@@ -48,7 +98,9 @@ exports.add = (0, task_1.createTask)({
|
|
|
48
98
|
await persistProfiles(profiles);
|
|
49
99
|
return {
|
|
50
100
|
status: 'Ok',
|
|
51
|
-
message: `Profile '${name}' added and set as default
|
|
101
|
+
message: `Profile '${name}' added and set as default`,
|
|
102
|
+
teamName: meInfo.teamName,
|
|
103
|
+
userName: meInfo.userName
|
|
52
104
|
};
|
|
53
105
|
}
|
|
54
106
|
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Profiles } from '../types';
|
|
2
|
+
export declare const clear: import("@forgehive/task").TaskInstanceType<(argv: {}, boundaries: import("@forgehive/task").WrappedBoundaries<{
|
|
3
|
+
loadProfiles: (args: {}) => Promise<Promise<Profiles>>;
|
|
4
|
+
clearProfiles: () => Promise<void>;
|
|
5
|
+
}>) => Promise<{
|
|
6
|
+
status: string;
|
|
7
|
+
message: string;
|
|
8
|
+
clearedCount?: undefined;
|
|
9
|
+
} | {
|
|
10
|
+
status: string;
|
|
11
|
+
message: string;
|
|
12
|
+
clearedCount: number;
|
|
13
|
+
}>, {
|
|
14
|
+
loadProfiles: (args: {}) => Promise<Promise<Profiles>>;
|
|
15
|
+
clearProfiles: () => Promise<void>;
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// TASK: clear
|
|
3
|
+
// Run this task with:
|
|
4
|
+
// forge task:run auth:clear
|
|
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.clear = void 0;
|
|
10
|
+
const task_1 = require("@forgehive/task");
|
|
11
|
+
const schema_1 = require("@forgehive/schema");
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
14
|
+
const os_1 = __importDefault(require("os"));
|
|
15
|
+
const load_1 = require("./load");
|
|
16
|
+
const schema = new schema_1.Schema({});
|
|
17
|
+
const boundaries = {
|
|
18
|
+
loadProfiles: load_1.load.asBoundary(),
|
|
19
|
+
clearProfiles: async () => {
|
|
20
|
+
const buildsPath = path_1.default.join(os_1.default.homedir(), '.forge');
|
|
21
|
+
const profilesPath = path_1.default.join(buildsPath, 'profiles.json');
|
|
22
|
+
// Create empty profiles structure
|
|
23
|
+
const emptyProfiles = {
|
|
24
|
+
default: '',
|
|
25
|
+
profiles: []
|
|
26
|
+
};
|
|
27
|
+
await promises_1.default.writeFile(profilesPath, JSON.stringify(emptyProfiles, null, 2));
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
exports.clear = (0, task_1.createTask)({
|
|
31
|
+
schema,
|
|
32
|
+
boundaries,
|
|
33
|
+
fn: async function (_argv, { loadProfiles, clearProfiles }) {
|
|
34
|
+
const profiles = await loadProfiles({});
|
|
35
|
+
if (profiles.profiles.length === 0) {
|
|
36
|
+
console.log('No profiles found to clear.');
|
|
37
|
+
return { status: 'Ok', message: 'No profiles found' };
|
|
38
|
+
}
|
|
39
|
+
const profileCount = profiles.profiles.length;
|
|
40
|
+
console.log(`Found ${profileCount} profile(s) to clear:`);
|
|
41
|
+
profiles.profiles.forEach(profile => {
|
|
42
|
+
console.log(` - ${profile.name} (${profile.teamName || 'Unknown team'})`);
|
|
43
|
+
});
|
|
44
|
+
console.log('\\nClearing all profiles...');
|
|
45
|
+
// Clear all profiles
|
|
46
|
+
await clearProfiles();
|
|
47
|
+
console.log('✅ All profiles cleared successfully');
|
|
48
|
+
return {
|
|
49
|
+
status: 'Ok',
|
|
50
|
+
message: `Cleared ${profileCount} profile(s)`,
|
|
51
|
+
clearedCount: profileCount
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
});
|
package/dist/tasks/auth/list.js
CHANGED
|
@@ -27,15 +27,23 @@ exports.list = (0, task_1.createTask)({
|
|
|
27
27
|
console.log(` Name: ${currentProfile.name}`);
|
|
28
28
|
console.log(` API Key: ${currentProfile.apiKey}`);
|
|
29
29
|
console.log(` URL: ${currentProfile.url}`);
|
|
30
|
+
if (currentProfile.userName) {
|
|
31
|
+
console.log(` User: ${currentProfile.userName}`);
|
|
32
|
+
}
|
|
33
|
+
if (currentProfile.teamName) {
|
|
34
|
+
console.log(` Team: ${currentProfile.teamName}`);
|
|
35
|
+
}
|
|
30
36
|
console.log('');
|
|
31
37
|
}
|
|
32
38
|
console.log('Available profiles:\n');
|
|
33
39
|
const tableData = profiles.profiles.map(profile => ({
|
|
34
40
|
Name: profile.name,
|
|
35
41
|
'API Key': profile.apiKey,
|
|
36
|
-
URL: profile.url
|
|
42
|
+
URL: profile.url,
|
|
43
|
+
Team: profile.teamName || 'Unknown',
|
|
44
|
+
User: profile.userName || 'Unknown'
|
|
37
45
|
}));
|
|
38
|
-
console.table(tableData, ['Name', 'API Key', 'URL']);
|
|
46
|
+
console.table(tableData, ['Name', 'API Key', 'URL', 'Team', 'User']);
|
|
39
47
|
console.log('\nUse auth:add to create or update a profile');
|
|
40
48
|
console.log('Use auth:switch [name] or auth:switch [index] to switch profiles');
|
|
41
49
|
console.log('========================================');
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { type ForgeConf } from '../types';
|
|
2
|
+
export declare const sync: import("@forgehive/task").TaskInstanceType<(argv: {}, boundaries: import("@forgehive/task").WrappedBoundaries<{
|
|
3
|
+
loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
|
|
4
|
+
loadCurrentProfile: (args: {}) => Promise<Promise<import("../types").Profile>>;
|
|
5
|
+
getCwd: () => Promise<string>;
|
|
6
|
+
persistConf: (forge: ForgeConf, cwd: string) => Promise<void>;
|
|
7
|
+
syncTasksToHive: (projectUuid: string, tasks: Array<{
|
|
8
|
+
uuid: string;
|
|
9
|
+
name: string;
|
|
10
|
+
}>, apiKey: string, apiSecret: string, baseUrl: string) => Promise<{
|
|
11
|
+
success: boolean;
|
|
12
|
+
error?: string;
|
|
13
|
+
data?: {
|
|
14
|
+
projectUuid: string;
|
|
15
|
+
projectName: string;
|
|
16
|
+
summary: {
|
|
17
|
+
total: number;
|
|
18
|
+
created: number;
|
|
19
|
+
updated: number;
|
|
20
|
+
errors: number;
|
|
21
|
+
};
|
|
22
|
+
results: {
|
|
23
|
+
created: Array<{
|
|
24
|
+
uuid: string;
|
|
25
|
+
taskName: string;
|
|
26
|
+
action: string;
|
|
27
|
+
}>;
|
|
28
|
+
updated: Array<{
|
|
29
|
+
uuid: string;
|
|
30
|
+
taskName: string;
|
|
31
|
+
previousName: string;
|
|
32
|
+
action: string;
|
|
33
|
+
}>;
|
|
34
|
+
errors: Array<{
|
|
35
|
+
uuid: string;
|
|
36
|
+
taskName: string;
|
|
37
|
+
error: string;
|
|
38
|
+
}>;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
}>;
|
|
42
|
+
}>) => Promise<{
|
|
43
|
+
status: string;
|
|
44
|
+
message: string;
|
|
45
|
+
summary?: undefined;
|
|
46
|
+
results?: undefined;
|
|
47
|
+
projectUrl?: undefined;
|
|
48
|
+
} | {
|
|
49
|
+
status: string;
|
|
50
|
+
summary: {
|
|
51
|
+
total: number;
|
|
52
|
+
created: number;
|
|
53
|
+
updated: number;
|
|
54
|
+
errors: number;
|
|
55
|
+
};
|
|
56
|
+
results: {
|
|
57
|
+
created: Array<{
|
|
58
|
+
uuid: string;
|
|
59
|
+
taskName: string;
|
|
60
|
+
action: string;
|
|
61
|
+
}>;
|
|
62
|
+
updated: Array<{
|
|
63
|
+
uuid: string;
|
|
64
|
+
taskName: string;
|
|
65
|
+
previousName: string;
|
|
66
|
+
action: string;
|
|
67
|
+
}>;
|
|
68
|
+
errors: Array<{
|
|
69
|
+
uuid: string;
|
|
70
|
+
taskName: string;
|
|
71
|
+
error: string;
|
|
72
|
+
}>;
|
|
73
|
+
};
|
|
74
|
+
projectUrl: string;
|
|
75
|
+
message?: undefined;
|
|
76
|
+
}>, {
|
|
77
|
+
loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
|
|
78
|
+
loadCurrentProfile: (args: {}) => Promise<Promise<import("../types").Profile>>;
|
|
79
|
+
getCwd: () => Promise<string>;
|
|
80
|
+
persistConf: (forge: ForgeConf, cwd: string) => Promise<void>;
|
|
81
|
+
syncTasksToHive: (projectUuid: string, tasks: Array<{
|
|
82
|
+
uuid: string;
|
|
83
|
+
name: string;
|
|
84
|
+
}>, apiKey: string, apiSecret: string, baseUrl: string) => Promise<{
|
|
85
|
+
success: boolean;
|
|
86
|
+
error?: string;
|
|
87
|
+
data?: {
|
|
88
|
+
projectUuid: string;
|
|
89
|
+
projectName: string;
|
|
90
|
+
summary: {
|
|
91
|
+
total: number;
|
|
92
|
+
created: number;
|
|
93
|
+
updated: number;
|
|
94
|
+
errors: number;
|
|
95
|
+
};
|
|
96
|
+
results: {
|
|
97
|
+
created: Array<{
|
|
98
|
+
uuid: string;
|
|
99
|
+
taskName: string;
|
|
100
|
+
action: string;
|
|
101
|
+
}>;
|
|
102
|
+
updated: Array<{
|
|
103
|
+
uuid: string;
|
|
104
|
+
taskName: string;
|
|
105
|
+
previousName: string;
|
|
106
|
+
action: string;
|
|
107
|
+
}>;
|
|
108
|
+
errors: Array<{
|
|
109
|
+
uuid: string;
|
|
110
|
+
taskName: string;
|
|
111
|
+
error: string;
|
|
112
|
+
}>;
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
}>;
|
|
116
|
+
}>;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// TASK: sync
|
|
3
|
+
// Run this task with:
|
|
4
|
+
// forge task:run project:sync
|
|
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.sync = void 0;
|
|
10
|
+
const task_1 = require("@forgehive/task");
|
|
11
|
+
const schema_1 = require("@forgehive/schema");
|
|
12
|
+
const uuid_1 = require("uuid");
|
|
13
|
+
const load_1 = require("../conf/load");
|
|
14
|
+
const loadCurrent_1 = require("../auth/loadCurrent");
|
|
15
|
+
const path_1 = __importDefault(require("path"));
|
|
16
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
17
|
+
const schema = new schema_1.Schema({});
|
|
18
|
+
const boundaries = {
|
|
19
|
+
loadConf: load_1.load.asBoundary(),
|
|
20
|
+
loadCurrentProfile: loadCurrent_1.loadCurrent.asBoundary(),
|
|
21
|
+
getCwd: async () => {
|
|
22
|
+
return process.cwd();
|
|
23
|
+
},
|
|
24
|
+
persistConf: async (forge, cwd) => {
|
|
25
|
+
const forgePath = path_1.default.join(cwd, 'forge.json');
|
|
26
|
+
await promises_1.default.writeFile(forgePath, JSON.stringify(forge, null, 2));
|
|
27
|
+
},
|
|
28
|
+
syncTasksToHive: async (projectUuid, tasks, apiKey, apiSecret, baseUrl) => {
|
|
29
|
+
try {
|
|
30
|
+
const url = `${baseUrl}/api/projects/${projectUuid}/sync`;
|
|
31
|
+
const response = await fetch(url, {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers: {
|
|
34
|
+
'Content-Type': 'application/json',
|
|
35
|
+
'Authorization': `Bearer ${apiKey}:${apiSecret}`
|
|
36
|
+
},
|
|
37
|
+
body: JSON.stringify({ tasks })
|
|
38
|
+
});
|
|
39
|
+
if (response.ok) {
|
|
40
|
+
const data = await response.json();
|
|
41
|
+
return { success: true, data };
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const errorData = await response.json().catch(() => ({ error: `HTTP ${response.status} - ${response.statusText}` }));
|
|
45
|
+
return { success: false, error: errorData.error || `HTTP ${response.status} - ${response.statusText}` };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return { success: false, error: error instanceof Error ? error.message : 'Network error' };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
exports.sync = (0, task_1.createTask)({
|
|
54
|
+
schema,
|
|
55
|
+
boundaries,
|
|
56
|
+
fn: async function (_argv, { loadConf, loadCurrentProfile, getCwd, persistConf, syncTasksToHive }) {
|
|
57
|
+
const cwd = await getCwd();
|
|
58
|
+
const forge = await loadConf({});
|
|
59
|
+
// Check if project has UUID
|
|
60
|
+
if (!forge.project.uuid) {
|
|
61
|
+
throw new Error('Project does not have a UUID. Please run "forge project:link" to connect to a Hive project.');
|
|
62
|
+
}
|
|
63
|
+
console.log(`
|
|
64
|
+
==================================================
|
|
65
|
+
Starting project sync to Hive!
|
|
66
|
+
Project: ${forge.project.name}
|
|
67
|
+
UUID: ${forge.project.uuid}
|
|
68
|
+
==================================================
|
|
69
|
+
`);
|
|
70
|
+
// Ensure all tasks have UUIDs and collect them for sync
|
|
71
|
+
let configUpdated = false;
|
|
72
|
+
const tasksToSync = [];
|
|
73
|
+
if (!forge.tasks) {
|
|
74
|
+
forge.tasks = {};
|
|
75
|
+
}
|
|
76
|
+
for (const [taskDescriptor, taskData] of Object.entries(forge.tasks)) {
|
|
77
|
+
if (!taskData.uuid) {
|
|
78
|
+
taskData.uuid = (0, uuid_1.v4)();
|
|
79
|
+
configUpdated = true;
|
|
80
|
+
console.log(` ➕ Generated UUID for task: ${taskDescriptor}`);
|
|
81
|
+
}
|
|
82
|
+
// Use the task descriptor (key) as the name for the API
|
|
83
|
+
const taskName = taskDescriptor;
|
|
84
|
+
tasksToSync.push({
|
|
85
|
+
uuid: taskData.uuid,
|
|
86
|
+
name: taskName
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
// Save config if we added UUIDs
|
|
90
|
+
if (configUpdated) {
|
|
91
|
+
await persistConf(forge, cwd);
|
|
92
|
+
console.log(' 💾 Updated forge.json with new UUIDs');
|
|
93
|
+
}
|
|
94
|
+
if (tasksToSync.length === 0) {
|
|
95
|
+
console.log(' ℹ️ No tasks found to sync');
|
|
96
|
+
return { status: 'no-tasks', message: 'No tasks found in project' };
|
|
97
|
+
}
|
|
98
|
+
console.log(` 📊 Found ${tasksToSync.length} tasks to sync`);
|
|
99
|
+
try {
|
|
100
|
+
const profile = await loadCurrentProfile({});
|
|
101
|
+
const result = await syncTasksToHive(forge.project.uuid, tasksToSync, profile.apiKey, profile.apiSecret, profile.url);
|
|
102
|
+
if (result.success && result.data) {
|
|
103
|
+
const { summary, results } = result.data;
|
|
104
|
+
console.log('\\n ✅ Sync completed successfully!');
|
|
105
|
+
console.log(` Total tasks: ${summary.total}`);
|
|
106
|
+
console.log(` Created: ${summary.created}`);
|
|
107
|
+
console.log(` Updated: ${summary.updated}`);
|
|
108
|
+
console.log(` Errors: ${summary.errors}`);
|
|
109
|
+
if (results.created.length > 0) {
|
|
110
|
+
console.log('\\n 🆕 Created tasks:');
|
|
111
|
+
results.created.forEach(task => {
|
|
112
|
+
console.log(` • ${task.taskName} (${task.uuid})`);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (results.updated.length > 0) {
|
|
116
|
+
console.log('\\n 🔄 Updated tasks:');
|
|
117
|
+
results.updated.forEach(task => {
|
|
118
|
+
console.log(` • ${task.taskName} (was: ${task.previousName}) (${task.uuid})`);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
if (results.errors.length > 0) {
|
|
122
|
+
console.log('\\n ❌ Tasks with errors:');
|
|
123
|
+
results.errors.forEach(task => {
|
|
124
|
+
console.log(` • ${task.taskName}: ${task.error} (${task.uuid})`);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
const projectUrl = `${profile.url}/dashboard/projects/${forge.project.uuid}`;
|
|
128
|
+
console.log(`\\n 🔗 View your project: ${projectUrl}`);
|
|
129
|
+
return {
|
|
130
|
+
status: 'success',
|
|
131
|
+
summary,
|
|
132
|
+
results,
|
|
133
|
+
projectUrl
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
throw new Error(result.error || 'Unknown sync error');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
142
|
+
if (errorMessage.includes('No default profile')) {
|
|
143
|
+
console.log('\\n ⚠️ No authentication profile found. Run "forge auth:add" to configure.');
|
|
144
|
+
return { status: 'no-auth', message: 'No authentication profile configured' };
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
console.log(`\\n ❌ Sync failed: ${errorMessage}`);
|
|
148
|
+
return { status: 'error', message: errorMessage };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
@@ -3,6 +3,7 @@ export declare const createTaskCommand: import("@forgehive/task").TaskInstanceTy
|
|
|
3
3
|
descriptorName: string;
|
|
4
4
|
}, boundaries: import("@forgehive/task").WrappedBoundaries<{
|
|
5
5
|
loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
|
|
6
|
+
loadCurrentProfile: (args: {}) => Promise<Promise<import("../types").Profile>>;
|
|
6
7
|
loadTemplate: () => Promise<string>;
|
|
7
8
|
getCwd: () => Promise<string>;
|
|
8
9
|
parseTaskName: (taskDescriptor: string) => Promise<TaskName>;
|
|
@@ -10,11 +11,17 @@ export declare const createTaskCommand: import("@forgehive/task").TaskInstanceTy
|
|
|
10
11
|
path: string;
|
|
11
12
|
}>;
|
|
12
13
|
persistConf: (forge: ForgeConf, cwd: string) => Promise<void>;
|
|
14
|
+
createTaskInHive: (projectUuid: string, taskUuid: string, taskName: string, description: string, apiKey: string, apiSecret: string, baseUrl: string) => Promise<{
|
|
15
|
+
success: boolean;
|
|
16
|
+
taskUrl?: string;
|
|
17
|
+
error?: string;
|
|
18
|
+
}>;
|
|
13
19
|
}>) => Promise<{
|
|
14
20
|
taskPath: string;
|
|
15
21
|
fileName: string;
|
|
16
22
|
}>, {
|
|
17
23
|
loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
|
|
24
|
+
loadCurrentProfile: (args: {}) => Promise<Promise<import("../types").Profile>>;
|
|
18
25
|
loadTemplate: () => Promise<string>;
|
|
19
26
|
getCwd: () => Promise<string>;
|
|
20
27
|
parseTaskName: (taskDescriptor: string) => Promise<TaskName>;
|
|
@@ -22,4 +29,9 @@ export declare const createTaskCommand: import("@forgehive/task").TaskInstanceTy
|
|
|
22
29
|
path: string;
|
|
23
30
|
}>;
|
|
24
31
|
persistConf: (forge: ForgeConf, cwd: string) => Promise<void>;
|
|
32
|
+
createTaskInHive: (projectUuid: string, taskUuid: string, taskName: string, description: string, apiKey: string, apiSecret: string, baseUrl: string) => Promise<{
|
|
33
|
+
success: boolean;
|
|
34
|
+
taskUrl?: string;
|
|
35
|
+
error?: string;
|
|
36
|
+
}>;
|
|
25
37
|
}>;
|
|
@@ -12,6 +12,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
12
12
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
13
13
|
const camelCase_1 = require("../../utils/camelCase");
|
|
14
14
|
const load_1 = require("../conf/load");
|
|
15
|
+
const loadCurrent_1 = require("../auth/loadCurrent");
|
|
15
16
|
// Define the template content directly in the code
|
|
16
17
|
// This eliminates the need to find and load an external file
|
|
17
18
|
const TASK_TEMPLATE = `// TASK: {{ taskName }}
|
|
@@ -55,6 +56,7 @@ const schema = new schema_1.Schema({
|
|
|
55
56
|
const boundaries = {
|
|
56
57
|
// Load boundaries
|
|
57
58
|
loadConf: load_1.load.asBoundary(),
|
|
59
|
+
loadCurrentProfile: loadCurrent_1.loadCurrent.asBoundary(),
|
|
58
60
|
loadTemplate: async () => {
|
|
59
61
|
return TASK_TEMPLATE;
|
|
60
62
|
},
|
|
@@ -100,12 +102,39 @@ const boundaries = {
|
|
|
100
102
|
persistConf: async (forge, cwd) => {
|
|
101
103
|
const forgePath = path_1.default.join(cwd, 'forge.json');
|
|
102
104
|
await promises_1.default.writeFile(forgePath, JSON.stringify(forge, null, 2));
|
|
105
|
+
},
|
|
106
|
+
createTaskInHive: async (projectUuid, taskUuid, taskName, description, apiKey, apiSecret, baseUrl) => {
|
|
107
|
+
try {
|
|
108
|
+
const url = `${baseUrl}/api/projects/${projectUuid}/tasks/${taskUuid}`;
|
|
109
|
+
const response = await fetch(url, {
|
|
110
|
+
method: 'POST',
|
|
111
|
+
headers: {
|
|
112
|
+
'Content-Type': 'application/json',
|
|
113
|
+
'Authorization': `Bearer ${apiKey}:${apiSecret}`
|
|
114
|
+
},
|
|
115
|
+
body: JSON.stringify({
|
|
116
|
+
taskName,
|
|
117
|
+
description
|
|
118
|
+
})
|
|
119
|
+
});
|
|
120
|
+
if (response.ok) {
|
|
121
|
+
const taskUrl = `${baseUrl}/tasks/${taskUuid}`;
|
|
122
|
+
return { success: true, taskUrl };
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
126
|
+
return { success: false, error: errorData.error || `HTTP ${response.status}` };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
return { success: false, error: error instanceof Error ? error.message : 'Network error' };
|
|
131
|
+
}
|
|
103
132
|
}
|
|
104
133
|
};
|
|
105
134
|
exports.createTaskCommand = (0, task_1.createTask)({
|
|
106
135
|
schema,
|
|
107
136
|
boundaries,
|
|
108
|
-
fn: async function ({ descriptorName }, { loadTemplate, persistTask, loadConf, persistConf, parseTaskName, getCwd }) {
|
|
137
|
+
fn: async function ({ descriptorName }, { loadTemplate, persistTask, loadConf, persistConf, parseTaskName, getCwd, loadCurrentProfile, createTaskInHive }) {
|
|
109
138
|
const { taskName, fileName, descriptor, dir } = await parseTaskName(descriptorName);
|
|
110
139
|
const cwd = await getCwd();
|
|
111
140
|
const forge = await loadConf({});
|
|
@@ -132,12 +161,32 @@ exports.createTaskCommand = (0, task_1.createTask)({
|
|
|
132
161
|
if (forge.tasks === undefined) {
|
|
133
162
|
forge.tasks = {};
|
|
134
163
|
}
|
|
164
|
+
const taskUuid = (0, uuid_1.v4)();
|
|
135
165
|
forge.tasks[descriptor] = {
|
|
136
166
|
path: `${taskPath}/${fileName}`,
|
|
137
167
|
handler: taskName,
|
|
138
|
-
uuid:
|
|
168
|
+
uuid: taskUuid
|
|
139
169
|
};
|
|
140
170
|
await persistConf(forge, cwd);
|
|
171
|
+
// Try to create task in Hive if user has profile and project UUID
|
|
172
|
+
if (forge.project.uuid) {
|
|
173
|
+
try {
|
|
174
|
+
const profile = await loadCurrentProfile({});
|
|
175
|
+
const result = await createTaskInHive(forge.project.uuid, taskUuid, descriptor, 'Add task description here', profile.apiKey, profile.apiSecret, profile.url);
|
|
176
|
+
if (result.success && result.taskUrl) {
|
|
177
|
+
console.log('\\n✅ Task created successfully in Hive!');
|
|
178
|
+
console.log(`🔗 View your task: ${result.taskUrl}`);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
console.log(`\n⚠️ Task created locally but could not sync to Hive: ${result.error}`);
|
|
182
|
+
console.log(`🔗 Host: ${profile.url}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
// Silently continue if no profile is configured
|
|
187
|
+
console.log('\\n📝 Task created locally. Configure a profile with \'forge auth:add\' to sync with Hive.');
|
|
188
|
+
}
|
|
189
|
+
}
|
|
141
190
|
return { taskPath, fileName };
|
|
142
191
|
}
|
|
143
192
|
});
|