@forgehive/forge-cli 0.3.10 → 0.3.12

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 (38) hide show
  1. package/dist/runner.js +32 -6
  2. package/dist/tasks/auth/add.d.ts +16 -0
  3. package/dist/tasks/auth/add.js +56 -4
  4. package/dist/tasks/auth/clear.d.ts +16 -0
  5. package/dist/tasks/auth/clear.js +54 -0
  6. package/dist/tasks/auth/list.d.ts +7 -1
  7. package/dist/tasks/auth/list.js +28 -8
  8. package/dist/tasks/auth/switch.js +24 -8
  9. package/dist/tasks/project/create.d.ts +27 -0
  10. package/dist/tasks/project/create.js +96 -0
  11. package/dist/tasks/project/link.d.ts +22 -0
  12. package/dist/tasks/project/link.js +95 -0
  13. package/dist/tasks/project/sync.d.ts +116 -0
  14. package/dist/tasks/project/sync.js +152 -0
  15. package/dist/tasks/project/unlink.d.ts +11 -0
  16. package/dist/tasks/project/unlink.js +65 -0
  17. package/dist/tasks/task/createTask.d.ts +12 -0
  18. package/dist/tasks/task/createTask.js +55 -5
  19. package/dist/tasks/task/run.d.ts +10 -2
  20. package/dist/tasks/task/run.js +14 -6
  21. package/dist/tasks/types.d.ts +4 -0
  22. package/dist/test/tasks/create.test.js +4 -3
  23. package/forge.json +14 -0
  24. package/package.json +6 -5
  25. package/src/runner.ts +32 -7
  26. package/src/tasks/auth/add.ts +66 -4
  27. package/src/tasks/auth/clear.ts +63 -0
  28. package/src/tasks/auth/list.ts +30 -8
  29. package/src/tasks/auth/switch.ts +24 -8
  30. package/src/tasks/project/README.md +268 -0
  31. package/src/tasks/project/create.ts +111 -0
  32. package/src/tasks/project/link.ts +106 -0
  33. package/src/tasks/project/sync.ts +202 -0
  34. package/src/tasks/project/unlink.ts +74 -0
  35. package/src/tasks/task/createTask.ts +72 -5
  36. package/src/tasks/task/run.ts +17 -6
  37. package/src/tasks/types.ts +4 -0
  38. package/src/test/tasks/create.test.ts +4 -3
@@ -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
+ });
@@ -0,0 +1,11 @@
1
+ import { type ForgeConf } from '../types';
2
+ export declare const unlink: import("@forgehive/task").TaskInstanceType<(argv: {}, boundaries: import("@forgehive/task").WrappedBoundaries<{
3
+ loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
4
+ writeFile: (filePath: string, content: string) => Promise<void>;
5
+ }>) => Promise<{
6
+ success: boolean;
7
+ unlinkedUuid: string;
8
+ }>, {
9
+ loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
10
+ writeFile: (filePath: string, content: string) => Promise<void>;
11
+ }>;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ // TASK: unlink
3
+ // Run this task with:
4
+ // forge project:unlink
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.unlink = void 0;
10
+ const task_1 = require("@forgehive/task");
11
+ const schema_1 = require("@forgehive/schema");
12
+ const promises_1 = __importDefault(require("fs/promises"));
13
+ const path_1 = __importDefault(require("path"));
14
+ const load_1 = require("../conf/load");
15
+ const name = 'project:unlink';
16
+ const description = 'Remove the project UUID link from local forge.json';
17
+ const schema = new schema_1.Schema({
18
+ // No parameters needed for unlink
19
+ });
20
+ const boundaries = {
21
+ loadConf: load_1.load.asBoundary(),
22
+ writeFile: async (filePath, content) => {
23
+ await promises_1.default.writeFile(filePath, content, 'utf-8');
24
+ }
25
+ };
26
+ exports.unlink = (0, task_1.createTask)({
27
+ name,
28
+ description,
29
+ schema,
30
+ boundaries,
31
+ fn: async function (argv, { loadConf, writeFile }) {
32
+ // Load current configuration
33
+ const conf = await loadConf({});
34
+ // Check if project has a UUID to unlink
35
+ if (!conf.project.uuid) {
36
+ throw new Error('No project UUID found in forge.json. The project is not currently linked to a remote project.');
37
+ }
38
+ const currentUuid = conf.project.uuid;
39
+ // Remove the UUID from the project configuration
40
+ const updatedConf = {
41
+ ...conf,
42
+ project: {
43
+ ...conf.project,
44
+ uuid: undefined
45
+ }
46
+ };
47
+ // Clean up undefined values by creating a new object without the uuid field
48
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
49
+ const { uuid, ...projectWithoutUuid } = updatedConf.project;
50
+ const finalConf = {
51
+ ...updatedConf,
52
+ project: projectWithoutUuid
53
+ };
54
+ // Write the updated configuration back to forge.json
55
+ const forgePath = path_1.default.join(process.cwd(), 'forge.json');
56
+ await writeFile(forgePath, JSON.stringify(finalConf, null, 2));
57
+ console.log(`✓ Successfully unlinked project from UUID: ${currentUuid}`);
58
+ console.log(' The project is no longer linked to a remote project.');
59
+ console.log(' You can now link to a different project using \'forge project:link [uuid]\'');
60
+ return {
61
+ success: true,
62
+ unlinkedUuid: currentUuid
63
+ };
64
+ }
65
+ });
@@ -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
  }>;
@@ -6,11 +6,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.createTaskCommand = void 0;
7
7
  const task_1 = require("@forgehive/task");
8
8
  const schema_1 = require("@forgehive/schema");
9
+ const uuid_1 = require("uuid");
9
10
  const handlebars_1 = __importDefault(require("handlebars"));
10
11
  const path_1 = __importDefault(require("path"));
11
12
  const promises_1 = __importDefault(require("fs/promises"));
12
13
  const camelCase_1 = require("../../utils/camelCase");
13
14
  const load_1 = require("../conf/load");
15
+ const loadCurrent_1 = require("../auth/loadCurrent");
14
16
  // Define the template content directly in the code
15
17
  // This eliminates the need to find and load an external file
16
18
  const TASK_TEMPLATE = `// TASK: {{ taskName }}
@@ -40,11 +42,10 @@ export const {{ taskName }} = createTask({
40
42
  boundaries,
41
43
  fn: async function (argv, boundaries) {
42
44
  console.log('input:', argv)
43
- console.log('boundaries:', boundaries)
45
+ console.log('boundaries:', Object.keys(boundaries))
44
46
  // Your task implementation goes here
45
- const status = { status: 'Ok' }
46
47
 
47
- return status
48
+ return {}
48
49
  }
49
50
  })
50
51
 
@@ -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,11 +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
- handler: taskName
167
+ handler: taskName,
168
+ uuid: taskUuid
138
169
  };
139
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
+ }
140
190
  return { taskPath, fileName };
141
191
  }
142
192
  });
@@ -17,7 +17,11 @@ export declare const run: import("@forgehive/task").TaskInstanceType<(argv: {
17
17
  }) => Promise<Promise<any>>;
18
18
  ensureLogFolder: (logsPath: string) => Promise<void>;
19
19
  ensureBuildsFolder: () => Promise<string>;
20
- sendLogToAPI: (profile: Profile, projectName: string, record: ExecutionRecord) => Promise<boolean>;
20
+ sendLogToAPI: (profile: Profile, projectName: string, record: ExecutionRecord, taskUuid?: string) => Promise<{
21
+ success: boolean;
22
+ logUuid?: string;
23
+ taskUuid?: string;
24
+ }>;
21
25
  }>) => Promise<any>, {
22
26
  loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
23
27
  loadCurrentProfile: (args: {}) => Promise<Promise<Profile>>;
@@ -32,5 +36,9 @@ export declare const run: import("@forgehive/task").TaskInstanceType<(argv: {
32
36
  }) => Promise<Promise<any>>;
33
37
  ensureLogFolder: (logsPath: string) => Promise<void>;
34
38
  ensureBuildsFolder: () => Promise<string>;
35
- sendLogToAPI: (profile: Profile, projectName: string, record: ExecutionRecord) => Promise<boolean>;
39
+ sendLogToAPI: (profile: Profile, projectName: string, record: ExecutionRecord, taskUuid?: string) => Promise<{
40
+ success: boolean;
41
+ logUuid?: string;
42
+ taskUuid?: string;
43
+ }>;
36
44
  }>;
@@ -50,7 +50,7 @@ const boundaries = {
50
50
  }
51
51
  return buildsPath;
52
52
  },
53
- sendLogToAPI: async (profile, projectName, record) => {
53
+ sendLogToAPI: async (profile, projectName, record, taskUuid) => {
54
54
  try {
55
55
  const config = {
56
56
  projectName,
@@ -63,21 +63,25 @@ const boundaries = {
63
63
  };
64
64
  const client = new hive_sdk_1.HiveLogClient(config);
65
65
  const result = await client.sendLog(record);
66
- if (result === 'success') {
66
+ if (result === 'success' || (typeof result === 'object' && 'uuid' in result)) {
67
67
  console.log('===============================================');
68
68
  console.log('Log sent to API... ', profile.name, profile.url);
69
- return true;
69
+ if (typeof result === 'object' && result && 'uuid' in result) {
70
+ const logResponse = result;
71
+ return { success: true, logUuid: logResponse.uuid, taskUuid };
72
+ }
73
+ return { success: true, taskUuid };
70
74
  }
71
75
  else {
72
76
  console.error('Failed to send log to API:', profile.url);
73
- return false;
77
+ return { success: false };
74
78
  }
75
79
  }
76
80
  catch (e) {
77
81
  console.error('Failed to send log to API:', profile.url);
78
82
  const error = e;
79
83
  console.error('Error:', error.message);
80
- return false;
84
+ return { success: false };
81
85
  }
82
86
  }
83
87
  };
@@ -89,6 +93,7 @@ exports.run = (0, task_1.createTask)({
89
93
  const forge = await loadConf({});
90
94
  const taskDescriptor = forge.tasks[descriptorName];
91
95
  const projectName = forge.project.name;
96
+ const taskUuid = taskDescriptor?.uuid;
92
97
  if (taskDescriptor === undefined) {
93
98
  throw new Error('Task is not defined on forge.json');
94
99
  }
@@ -149,7 +154,10 @@ exports.run = (0, task_1.createTask)({
149
154
  await tape.save();
150
155
  if (profile) {
151
156
  try {
152
- await sendLogToAPI(profile, projectName, logItem);
157
+ const logResult = await sendLogToAPI(profile, projectName, logItem, taskUuid);
158
+ if (logResult.success && taskUuid) {
159
+ console.log(`🔗 View execution logs: ${profile.url}/tasks/${taskUuid}?tab=logs`);
160
+ }
153
161
  }
154
162
  catch (e) {
155
163
  console.error('Failed to send log to API:', e);
@@ -9,6 +9,7 @@ export interface RunnerDescriptor {
9
9
  export interface TaskDescriptor {
10
10
  path: string;
11
11
  handler: string;
12
+ uuid?: string;
12
13
  }
13
14
  export interface Infra {
14
15
  region: string;
@@ -42,6 +43,9 @@ export interface Profile {
42
43
  apiKey: string;
43
44
  apiSecret: string;
44
45
  url: string;
46
+ teamName?: string;
47
+ teamUuid?: string;
48
+ userName?: string;
45
49
  }
46
50
  export interface Profiles {
47
51
  default: string;
@@ -35,11 +35,10 @@ export const newTask = createTask({
35
35
  boundaries,
36
36
  fn: async function (argv, boundaries) {
37
37
  console.log('input:', argv)
38
- console.log('boundaries:', boundaries)
38
+ console.log('boundaries:', Object.keys(boundaries))
39
39
  // Your task implementation goes here
40
- const status = { status: 'Ok' }
41
40
 
42
- return status
41
+ return {}
43
42
  }
44
43
  })
45
44
 
@@ -94,5 +93,7 @@ describe('Create task', () => {
94
93
  const forgeContent = await fs.promises.readFile(path_1.default.join(rootDir, 'forge.json'), 'utf-8');
95
94
  const forgeConf = JSON.parse(forgeContent);
96
95
  expect(forgeConf.tasks['sample:newTask']).toBeDefined();
96
+ expect(forgeConf.tasks['sample:newTask'].uuid).toBeDefined();
97
+ expect(typeof forgeConf.tasks['sample:newTask'].uuid).toBe('string');
97
98
  });
98
99
  });
package/forge.json CHANGED
@@ -118,6 +118,20 @@
118
118
  "docs:download": {
119
119
  "path": "src/tasks/docs/download.ts",
120
120
  "handler": "download"
121
+ },
122
+ "project:create": {
123
+ "path": "src/tasks/project/create.ts",
124
+ "handler": "create"
125
+ },
126
+ "project:link": {
127
+ "path": "src/tasks/project/link.ts",
128
+ "handler": "link",
129
+ "uuid": "cb9f82e1-d397-46d9-9f0d-2b0e3becbfa1"
130
+ },
131
+ "project:unlink": {
132
+ "path": "src/tasks/project/unlink.ts",
133
+ "handler": "unlink",
134
+ "uuid": "414d37de-793c-4d01-899d-69515f5e0948"
121
135
  }
122
136
  },
123
137
  "runners": {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgehive/forge-cli",
3
- "version": "0.3.10",
3
+ "version": "0.3.12",
4
4
  "description": "TypeScript CLI application",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -10,7 +10,7 @@
10
10
  "publishConfig": {
11
11
  "access": "public",
12
12
  "dependencies": {
13
- "@forgehive/hive-sdk": "^0.1.2",
13
+ "@forgehive/hive-sdk": "^0.1.3",
14
14
  "@forgehive/record-tape": "^0.2.5",
15
15
  "@forgehive/runner": "^0.2.5",
16
16
  "@forgehive/schema": "^0.1.4",
@@ -29,11 +29,12 @@
29
29
  "handlebars": "^4.7.8",
30
30
  "minimist": "^1.2.8",
31
31
  "typescript": "^5.3.3",
32
- "@forgehive/hive-sdk": "0.1.2",
32
+ "uuid": "^11.1.0",
33
+ "@forgehive/hive-sdk": "0.1.3",
33
34
  "@forgehive/record-tape": "0.2.5",
34
- "@forgehive/task": "0.2.5",
35
35
  "@forgehive/runner": "0.2.5",
36
- "@forgehive/schema": "0.1.4"
36
+ "@forgehive/schema": "0.1.4",
37
+ "@forgehive/task": "0.2.5"
37
38
  },
38
39
  "devDependencies": {
39
40
  "@types/archiver": "^6.0.3",