@forgehive/forge-cli 0.3.17 → 0.3.18

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 CHANGED
@@ -92,7 +92,7 @@ runner.setHandler(async (data) => {
92
92
  const commandsWithDescriptor = ['task:create', 'task:remove', 'task:publish', 'task:describe', 'task:fingerprint'];
93
93
  const commandsWithRunner = ['runner:create', 'runner:remove'];
94
94
  const commandsWithoutParams = ['project:unlink', 'project:sync', 'auth:clear'];
95
- const silentCommands = ['task:describe', 'task:list', 'auth:list', 'info'];
95
+ const silentCommands = ['task:describe', 'task:list', 'auth:list', 'info', 'docs:download'];
96
96
  if (commandsWithDescriptor.includes(taskName)) {
97
97
  result = await task.run({ descriptorName: action });
98
98
  }
@@ -170,9 +170,9 @@ runner.setHandler(async (data) => {
170
170
  });
171
171
  }
172
172
  else if (taskName === 'project:create') {
173
- const { projectName, description } = args;
173
+ const { name, description } = args;
174
174
  result = await task.run({
175
- projectName,
175
+ name,
176
176
  description
177
177
  });
178
178
  }
@@ -117,6 +117,7 @@ exports.download = (0, task_1.createTask)({
117
117
  });
118
118
  // Download Hive logging guide if --logs flag is provided
119
119
  if (logs) {
120
+ console.log('===============================================');
120
121
  const logsTargetPath = customPath
121
122
  ? path_1.default.join(path_1.default.dirname(customPath), 'hive-logging.md')
122
123
  : 'docs/hive-logging.md';
@@ -142,6 +143,7 @@ exports.download = (0, task_1.createTask)({
142
143
  size: logsContent.length
143
144
  });
144
145
  }
146
+ console.log('===============================================');
145
147
  return {
146
148
  success: true,
147
149
  downloads: results,
@@ -1,7 +1,7 @@
1
1
  import { type ForgeConf, type Profile } from '../types';
2
2
  export declare const create: import("@forgehive/task").TaskInstanceType<(argv: {
3
+ name: string;
3
4
  description?: string | undefined;
4
- projectName?: string | undefined;
5
5
  }, boundaries: import("@forgehive/task").WrappedBoundaries<{
6
6
  loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
7
7
  loadCurrentProfile: (args: {}) => Promise<Promise<Profile>>;
@@ -14,10 +14,10 @@ const promises_1 = __importDefault(require("fs/promises"));
14
14
  const path_1 = __importDefault(require("path"));
15
15
  const load_1 = require("../conf/load");
16
16
  const loadCurrent_1 = require("../auth/loadCurrent");
17
- const name = 'project:create';
18
- const description = 'Create a new project in ForgeHive';
17
+ const taskName = 'project:create';
18
+ const taskDescription = 'Create a new project in ForgeHive';
19
19
  const schema = new schema_1.Schema({
20
- projectName: schema_1.Schema.string().optional(),
20
+ name: schema_1.Schema.string(),
21
21
  description: schema_1.Schema.string().optional()
22
22
  });
23
23
  const boundaries = {
@@ -39,33 +39,29 @@ const boundaries = {
39
39
  }
40
40
  };
41
41
  exports.create = (0, task_1.createTask)({
42
- name,
43
- description,
42
+ name: taskName,
43
+ description: taskDescription,
44
44
  schema,
45
45
  boundaries,
46
46
  fn: async function (argv, { loadConf, loadCurrentProfile, writeFile, createProject }) {
47
- const { projectName: inputProjectName, description } = argv;
47
+ const { name: projectName, description } = argv;
48
48
  // Load current configuration
49
49
  const conf = await loadConf({});
50
- // Use provided projectName or fall back to forge.json project name
51
- const projectName = inputProjectName || conf.project.name;
52
- if (!projectName) {
53
- throw new Error('Project name is required. Provide --projectName or ensure forge.json has a project name.');
54
- }
55
50
  // Check if project already has a UUID, generate one if not
56
- let projectUuid = conf.project.uuid;
57
- if (!projectUuid) {
58
- projectUuid = (0, uuid_1.v4)();
59
- // Update forge.json with the new UUID
60
- const forgePath = path_1.default.join(process.cwd(), 'forge.json');
61
- const updatedConf = {
62
- ...conf,
63
- project: {
64
- ...conf.project,
65
- uuid: projectUuid
66
- }
67
- };
68
- await writeFile(forgePath, JSON.stringify(updatedConf, null, 2));
51
+ const projectUuid = conf.project.uuid || (0, uuid_1.v4)();
52
+ // Update forge.json with the project name and UUID
53
+ const forgePath = path_1.default.join(process.cwd(), 'forge.json');
54
+ const updatedConf = {
55
+ ...conf,
56
+ project: {
57
+ ...conf.project,
58
+ name: projectName,
59
+ uuid: projectUuid
60
+ }
61
+ };
62
+ await writeFile(forgePath, JSON.stringify(updatedConf, null, 2));
63
+ console.log(`Updated forge.json with project name: ${projectName}`);
64
+ if (!conf.project.uuid) {
69
65
  console.log(`Generated and saved project UUID: ${projectUuid}`);
70
66
  }
71
67
  // Load current profile for API authentication
@@ -4,7 +4,7 @@ export declare const sync: import("@forgehive/task").TaskInstanceType<(argv: {},
4
4
  loadCurrentProfile: (args: {}) => Promise<Promise<import("../types").Profile>>;
5
5
  getCwd: () => Promise<string>;
6
6
  persistConf: (forge: ForgeConf, cwd: string) => Promise<void>;
7
- syncTasksToHive: (projectUuid: string, tasks: Array<{
7
+ syncTasksToHive: (projectUuid: string, projectName: string, projectDescription: string | undefined, tasks: Array<{
8
8
  uuid: string;
9
9
  name: string;
10
10
  }>, apiKey: string, apiSecret: string, baseUrl: string) => Promise<{
@@ -78,7 +78,7 @@ export declare const sync: import("@forgehive/task").TaskInstanceType<(argv: {},
78
78
  loadCurrentProfile: (args: {}) => Promise<Promise<import("../types").Profile>>;
79
79
  getCwd: () => Promise<string>;
80
80
  persistConf: (forge: ForgeConf, cwd: string) => Promise<void>;
81
- syncTasksToHive: (projectUuid: string, tasks: Array<{
81
+ syncTasksToHive: (projectUuid: string, projectName: string, projectDescription: string | undefined, tasks: Array<{
82
82
  uuid: string;
83
83
  name: string;
84
84
  }>, apiKey: string, apiSecret: string, baseUrl: string) => Promise<{
@@ -25,7 +25,7 @@ const boundaries = {
25
25
  const forgePath = path_1.default.join(cwd, 'forge.json');
26
26
  await promises_1.default.writeFile(forgePath, JSON.stringify(forge, null, 2));
27
27
  },
28
- syncTasksToHive: async (projectUuid, tasks, apiKey, apiSecret, baseUrl) => {
28
+ syncTasksToHive: async (projectUuid, projectName, projectDescription, tasks, apiKey, apiSecret, baseUrl) => {
29
29
  try {
30
30
  const url = `${baseUrl}/api/projects/${projectUuid}/sync`;
31
31
  const response = await fetch(url, {
@@ -34,7 +34,7 @@ const boundaries = {
34
34
  'Content-Type': 'application/json',
35
35
  'Authorization': `Bearer ${apiKey}:${apiSecret}`
36
36
  },
37
- body: JSON.stringify({ tasks })
37
+ body: JSON.stringify({ projectName, description: projectDescription, tasks })
38
38
  });
39
39
  if (response.ok) {
40
40
  const data = await response.json();
@@ -96,9 +96,13 @@ exports.sync = (0, task_1.createTask)({
96
96
  return { status: 'no-tasks', message: 'No tasks found in project' };
97
97
  }
98
98
  console.log(` 📊 Found ${tasksToSync.length} tasks to sync`);
99
+ console.log(` 📝 Project name: ${forge.project.name}`);
100
+ if (forge.project.description) {
101
+ console.log(` 📝 Project description: ${forge.project.description}`);
102
+ }
99
103
  try {
100
104
  const profile = await loadCurrentProfile({});
101
- const result = await syncTasksToHive(forge.project.uuid, tasksToSync, profile.apiKey, profile.apiSecret, profile.url);
105
+ const result = await syncTasksToHive(forge.project.uuid, forge.project.name, forge.project.description, tasksToSync, profile.apiKey, profile.apiSecret, profile.url);
102
106
  if (result.success && result.data) {
103
107
  const { summary, results } = result.data;
104
108
  console.log('\\n ✅ Sync completed successfully!');
@@ -1,3 +1,4 @@
1
+ import { type ExecutionRecord } from '@forgehive/hive-sdk';
1
2
  import { type ForgeConf, type Profile } from '../types';
2
3
  interface Fixture {
3
4
  fixtureUUID: string;
@@ -28,7 +29,10 @@ export declare const replay: import("@forgehive/task").TaskInstanceType<(argv: {
28
29
  }) => Promise<Promise<any>>;
29
30
  ensureBuildsFolder: () => Promise<string>;
30
31
  verifyLogFolder: (logsPath: string) => Promise<boolean>;
31
- sendLogToAPI: (profile: Profile, projectName: string, taskName: string, logItem: unknown, fixtureUUID: string) => Promise<boolean>;
32
+ sendLogToAPI: (profile: Profile, record: ExecutionRecord, fixtureUUID: string) => Promise<{
33
+ success: boolean;
34
+ logUuid?: string;
35
+ }>;
32
36
  }>) => Promise<any>, {
33
37
  readFixture: (filePath: string) => Promise<Fixture>;
34
38
  loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
@@ -44,6 +48,9 @@ export declare const replay: import("@forgehive/task").TaskInstanceType<(argv: {
44
48
  }) => Promise<Promise<any>>;
45
49
  ensureBuildsFolder: () => Promise<string>;
46
50
  verifyLogFolder: (logsPath: string) => Promise<boolean>;
47
- sendLogToAPI: (profile: Profile, projectName: string, taskName: string, logItem: unknown, fixtureUUID: string) => Promise<boolean>;
51
+ sendLogToAPI: (profile: Profile, record: ExecutionRecord, fixtureUUID: string) => Promise<{
52
+ success: boolean;
53
+ logUuid?: string;
54
+ }>;
48
55
  }>;
49
56
  export {};
@@ -12,7 +12,7 @@ const schema_1 = require("@forgehive/schema");
12
12
  const promises_1 = __importDefault(require("fs/promises"));
13
13
  const path_1 = __importDefault(require("path"));
14
14
  const os_1 = __importDefault(require("os"));
15
- const axios_1 = __importDefault(require("axios"));
15
+ const hive_sdk_1 = require("@forgehive/hive-sdk");
16
16
  const create_1 = require("../bundle/create");
17
17
  const load_1 = require("../bundle/load");
18
18
  const load_2 = require("../conf/load");
@@ -53,30 +53,41 @@ const boundaries = {
53
53
  }
54
54
  return true;
55
55
  },
56
- sendLogToAPI: async (profile, projectName, taskName, logItem, fixtureUUID) => {
56
+ sendLogToAPI: async (profile, record, fixtureUUID) => {
57
57
  try {
58
- const logsUrl = `${profile.url}/api/tasks/log-ingest`;
59
- const authToken = `${profile.apiKey}:${profile.apiSecret}`;
60
- await axios_1.default.post(logsUrl, {
61
- projectName,
62
- taskName,
63
- logItem: JSON.stringify(logItem),
64
- replayFrom: fixtureUUID
65
- }, {
66
- headers: {
67
- Authorization: `Bearer ${authToken}`,
68
- 'Content-Type': 'application/json'
58
+ // Use createClientFromForgeConf to automatically load forge.json with task UUIDs
59
+ const client = (0, hive_sdk_1.createClientFromForgeConf)('./forge.json', {
60
+ apiKey: profile.apiKey,
61
+ apiSecret: profile.apiSecret,
62
+ host: profile.url,
63
+ metadata: {
64
+ environment: 'cli',
65
+ replayFrom: fixtureUUID
69
66
  }
70
67
  });
71
68
  console.log('===============================================');
72
- console.log('Log sent to API... ', profile.name, profile.url);
73
- console.log('Replay from fixture UUID:', fixtureUUID);
74
- return true;
69
+ console.log('Sending replay log to Hive...');
70
+ const result = await client.sendLog(record);
71
+ if (result === 'success' || (typeof result === 'object' && 'uuid' in result)) {
72
+ console.log('✅ Log sent to API...', profile.name, profile.url);
73
+ console.log('Replay from fixture UUID:', fixtureUUID);
74
+ console.log('===============================================');
75
+ if (typeof result === 'object' && result && 'uuid' in result) {
76
+ return { success: true, logUuid: result.uuid };
77
+ }
78
+ return { success: true };
79
+ }
80
+ else {
81
+ console.error('❌ Failed to send log to Hive');
82
+ console.log('===============================================');
83
+ return { success: false };
84
+ }
75
85
  }
76
86
  catch (e) {
77
87
  const error = e;
78
- console.error('Failed to send log to API:', error.message);
79
- return false;
88
+ console.error('Failed to send log to API:', error.message);
89
+ console.log('===============================================');
90
+ return { success: false };
80
91
  }
81
92
  }
82
93
  };
@@ -90,7 +101,6 @@ exports.replay = (0, task_1.createTask)({
90
101
  // Load forge configuration
91
102
  const forge = await loadConf({});
92
103
  const taskDescriptor = forge.tasks[descriptorName];
93
- const projectName = forge.project.name;
94
104
  if (taskDescriptor === undefined) {
95
105
  throw new Error(`Task ${descriptorName} is not defined in forge.json`);
96
106
  }
@@ -168,7 +178,7 @@ exports.replay = (0, task_1.createTask)({
168
178
  // Send the log to API if profile is available
169
179
  if (profile) {
170
180
  try {
171
- await sendLogToAPI(profile, projectName, descriptorName, record, fixture.fixtureUUID);
181
+ await sendLogToAPI(profile, record, fixture.fixtureUUID);
172
182
  }
173
183
  catch (e) {
174
184
  console.error('Failed to send log to API:', e);
@@ -17,7 +17,7 @@ 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, taskUuid?: string, projectUuid?: string) => Promise<{
20
+ sendLogToAPI: (profile: Profile, record: ExecutionRecord, taskUuid?: string) => Promise<{
21
21
  success: boolean;
22
22
  logUuid?: string;
23
23
  taskUuid?: string;
@@ -37,7 +37,7 @@ export declare const run: import("@forgehive/task").TaskInstanceType<(argv: {
37
37
  }) => Promise<Promise<any>>;
38
38
  ensureLogFolder: (logsPath: string) => Promise<void>;
39
39
  ensureBuildsFolder: () => Promise<string>;
40
- sendLogToAPI: (profile: Profile, projectName: string, record: ExecutionRecord, taskUuid?: string, projectUuid?: string) => Promise<{
40
+ sendLogToAPI: (profile: Profile, record: ExecutionRecord, taskUuid?: string) => Promise<{
41
41
  success: boolean;
42
42
  logUuid?: string;
43
43
  taskUuid?: string;
@@ -50,39 +50,32 @@ const boundaries = {
50
50
  }
51
51
  return buildsPath;
52
52
  },
53
- sendLogToAPI: async (profile, projectName, record, taskUuid, projectUuid) => {
54
- // Check if we have required UUIDs for the new endpoint
55
- if (!projectUuid || !taskUuid) {
53
+ sendLogToAPI: async (profile, record, taskUuid) => {
54
+ // Check if we have required UUID for the task
55
+ if (!taskUuid) {
56
56
  console.log('===============================================');
57
- console.log('⚠️ Remote logging skipped - missing UUIDs');
57
+ console.log('⚠️ Remote logging skipped - missing task UUID');
58
58
  console.log('');
59
59
  console.log('To enable remote logging with enhanced features:');
60
- if (!projectUuid) {
61
- console.log('• Use "forge project:create" to create a new project, or');
62
- console.log('• Use "forge project:link" to connect to an existing project');
63
- }
64
- if (!taskUuid) {
65
- console.log('• Use "forge project:sync" to get the task to have UUID');
66
- }
60
+ console.log('• Use "forge project:sync" to sync tasks and get UUIDs');
67
61
  console.log('===============================================');
68
62
  return { success: true, skipRemoteLog: true };
69
63
  }
70
64
  try {
71
- const config = {
72
- projectName,
73
- projectUuid,
65
+ // Use createClientFromForgeConf to automatically load forge.json with task UUIDs
66
+ const client = (0, hive_sdk_1.createClientFromForgeConf)('./forge.json', {
74
67
  apiKey: profile.apiKey,
75
68
  apiSecret: profile.apiSecret,
76
69
  host: profile.url,
77
70
  metadata: {
78
71
  environment: 'cli'
79
72
  }
80
- };
81
- const client = new hive_sdk_1.HiveLogClient(config);
73
+ });
74
+ console.log('===============================================');
82
75
  console.log('Sending execution log to Hive...');
83
- const result = await client.sendLogByUuid(record, taskUuid);
76
+ const result = await client.sendLog(record);
84
77
  if (result === 'success' || (typeof result === 'object' && 'uuid' in result)) {
85
- console.log('===============================================');
78
+ console.log('');
86
79
  console.log('✅ Log sent to Hive successfully');
87
80
  console.log(` Profile: ${profile.name}`);
88
81
  console.log(` Host: ${profile.url}`);
@@ -93,14 +86,18 @@ const boundaries = {
93
86
  return { success: true, taskUuid };
94
87
  }
95
88
  else {
89
+ console.log('');
96
90
  console.error('❌ Failed to send log to Hive:', profile.url);
91
+ console.log('===============================================');
97
92
  return { success: false };
98
93
  }
99
94
  }
100
95
  catch (e) {
96
+ console.log('===============================================');
101
97
  console.error('❌ Failed to send log to Hive:', profile.url);
102
98
  const error = e;
103
99
  console.error('Error:', error.message);
100
+ console.log('===============================================');
104
101
  return { success: false };
105
102
  }
106
103
  }
@@ -112,8 +109,6 @@ exports.run = (0, task_1.createTask)({
112
109
  // Load forge configuration
113
110
  const forge = await loadConf({});
114
111
  const taskDescriptor = forge.tasks[descriptorName];
115
- const projectName = forge.project.name;
116
- const projectUuid = forge.project.uuid;
117
112
  const taskUuid = taskDescriptor?.uuid;
118
113
  if (taskDescriptor === undefined) {
119
114
  throw new Error('Task is not defined on forge.json');
@@ -175,7 +170,7 @@ exports.run = (0, task_1.createTask)({
175
170
  await tape.save();
176
171
  if (profile) {
177
172
  try {
178
- const logResult = await sendLogToAPI(profile, projectName, logItem, taskUuid, projectUuid);
173
+ const logResult = await sendLogToAPI(profile, logItem, taskUuid);
179
174
  if (logResult.success && !logResult.skipRemoteLog && taskUuid) {
180
175
  console.log(`🔗 View execution logs: ${profile.url}/tasks/${taskUuid}?tab=logs`);
181
176
  }
@@ -19,6 +19,7 @@ export interface ForgeConf {
19
19
  project: {
20
20
  name: string;
21
21
  uuid?: string;
22
+ description?: string;
22
23
  };
23
24
  paths: {
24
25
  logs: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgehive/forge-cli",
3
- "version": "0.3.17",
3
+ "version": "0.3.18",
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.6",
13
+ "@forgehive/hive-sdk": "^0.2.0",
14
14
  "@forgehive/record-tape": "^0.2.7",
15
15
  "@forgehive/runner": "^0.2.7",
16
16
  "@forgehive/schema": "^0.1.4",
@@ -30,11 +30,11 @@
30
30
  "minimist": "^1.2.8",
31
31
  "typescript": "^5.3.3",
32
32
  "uuid": "^11.1.0",
33
- "@forgehive/runner": "0.2.7",
34
- "@forgehive/schema": "0.1.4",
35
- "@forgehive/task": "0.2.7",
36
33
  "@forgehive/record-tape": "0.2.7",
37
- "@forgehive/hive-sdk": "0.1.6"
34
+ "@forgehive/task": "0.2.7",
35
+ "@forgehive/hive-sdk": "0.2.0",
36
+ "@forgehive/schema": "0.1.4",
37
+ "@forgehive/runner": "0.2.7"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/archiver": "^6.0.3",
package/src/runner.ts CHANGED
@@ -114,7 +114,7 @@ runner.setHandler(async (data: ParsedArgs): Promise<unknown> => {
114
114
  const commandsWithDescriptor = ['task:create', 'task:remove', 'task:publish', 'task:describe', 'task:fingerprint']
115
115
  const commandsWithRunner = ['runner:create', 'runner:remove']
116
116
  const commandsWithoutParams = ['project:unlink', 'project:sync', 'auth:clear']
117
- const silentCommands = ['task:describe', 'task:list', 'auth:list', 'info']
117
+ const silentCommands = ['task:describe', 'task:list', 'auth:list', 'info', 'docs:download']
118
118
 
119
119
  if (commandsWithDescriptor.includes(taskName)) {
120
120
  result = await task.run({ descriptorName: action })
@@ -191,10 +191,10 @@ runner.setHandler(async (data: ParsedArgs): Promise<unknown> => {
191
191
  logs
192
192
  })
193
193
  } else if (taskName === 'project:create') {
194
- const { projectName, description } = args as { projectName?: string, description?: string }
194
+ const { name, description } = args as { name: string, description?: string }
195
195
 
196
196
  result = await task.run({
197
- projectName,
197
+ name,
198
198
  description
199
199
  })
200
200
  } else if (taskName === 'project:link') {
@@ -92,6 +92,7 @@ export const download = createTask({
92
92
 
93
93
  // Download Hive logging guide if --logs flag is provided
94
94
  if (logs) {
95
+ console.log('===============================================')
95
96
  const logsTargetPath = customPath
96
97
  ? path.join(path.dirname(customPath), 'hive-logging.md')
97
98
  : 'docs/hive-logging.md'
@@ -125,6 +126,8 @@ export const download = createTask({
125
126
  })
126
127
  }
127
128
 
129
+ console.log('===============================================')
130
+
128
131
  return {
129
132
  success: true,
130
133
  downloads: results,
@@ -12,11 +12,11 @@ import { load as loadConf } from '../conf/load'
12
12
  import { loadCurrent as loadCurrentProfile } from '../auth/loadCurrent'
13
13
  import { type ForgeConf, type Profile } from '../types'
14
14
 
15
- const name = 'project:create'
16
- const description = 'Create a new project in ForgeHive'
15
+ const taskName = 'project:create'
16
+ const taskDescription = 'Create a new project in ForgeHive'
17
17
 
18
18
  const schema = new Schema({
19
- projectName: Schema.string().optional(),
19
+ name: Schema.string(),
20
20
  description: Schema.string().optional()
21
21
  })
22
22
 
@@ -40,39 +40,33 @@ const boundaries = {
40
40
  }
41
41
 
42
42
  export const create = createTask({
43
- name,
44
- description,
43
+ name: taskName,
44
+ description: taskDescription,
45
45
  schema,
46
46
  boundaries,
47
47
  fn: async function (argv, { loadConf, loadCurrentProfile, writeFile, createProject }) {
48
- const { projectName: inputProjectName, description } = argv
48
+ const { name: projectName, description } = argv
49
49
 
50
50
  // Load current configuration
51
51
  const conf = await loadConf({})
52
52
 
53
- // Use provided projectName or fall back to forge.json project name
54
- const projectName = inputProjectName || conf.project.name
55
-
56
- if (!projectName) {
57
- throw new Error('Project name is required. Provide --projectName or ensure forge.json has a project name.')
58
- }
59
-
60
53
  // Check if project already has a UUID, generate one if not
61
- let projectUuid = conf.project.uuid
62
- if (!projectUuid) {
63
- projectUuid = uuidv4()
64
-
65
- // Update forge.json with the new UUID
66
- const forgePath = path.join(process.cwd(), 'forge.json')
67
- const updatedConf: ForgeConf = {
68
- ...conf,
69
- project: {
70
- ...conf.project,
71
- uuid: projectUuid
72
- }
54
+ const projectUuid = conf.project.uuid || uuidv4()
55
+
56
+ // Update forge.json with the project name and UUID
57
+ const forgePath = path.join(process.cwd(), 'forge.json')
58
+ const updatedConf: ForgeConf = {
59
+ ...conf,
60
+ project: {
61
+ ...conf.project,
62
+ name: projectName,
63
+ uuid: projectUuid
73
64
  }
65
+ }
74
66
 
75
- await writeFile(forgePath, JSON.stringify(updatedConf, null, 2))
67
+ await writeFile(forgePath, JSON.stringify(updatedConf, null, 2))
68
+ console.log(`Updated forge.json with project name: ${projectName}`)
69
+ if (!conf.project.uuid) {
76
70
  console.log(`Generated and saved project UUID: ${projectUuid}`)
77
71
  }
78
72
 
@@ -26,6 +26,8 @@ const boundaries = {
26
26
  },
27
27
  syncTasksToHive: async (
28
28
  projectUuid: string,
29
+ projectName: string,
30
+ projectDescription: string | undefined,
29
31
  tasks: Array<{ uuid: string; name: string }>,
30
32
  apiKey: string,
31
33
  apiSecret: string,
@@ -57,7 +59,7 @@ const boundaries = {
57
59
  'Content-Type': 'application/json',
58
60
  'Authorization': `Bearer ${apiKey}:${apiSecret}`
59
61
  },
60
- body: JSON.stringify({ tasks })
62
+ body: JSON.stringify({ projectName, description: projectDescription, tasks })
61
63
  })
62
64
 
63
65
  if (response.ok) {
@@ -134,11 +136,17 @@ export const sync = createTask({
134
136
  }
135
137
 
136
138
  console.log(` 📊 Found ${tasksToSync.length} tasks to sync`)
139
+ console.log(` 📝 Project name: ${forge.project.name}`)
140
+ if (forge.project.description) {
141
+ console.log(` 📝 Project description: ${forge.project.description}`)
142
+ }
137
143
 
138
144
  try {
139
145
  const profile = await loadCurrentProfile({})
140
146
  const result = await syncTasksToHive(
141
147
  forge.project.uuid,
148
+ forge.project.name,
149
+ forge.project.description,
142
150
  tasksToSync,
143
151
  profile.apiKey,
144
152
  profile.apiSecret,
@@ -7,8 +7,8 @@ import { Schema } from '@forgehive/schema'
7
7
  import fs from 'fs/promises'
8
8
  import path from 'path'
9
9
  import os from 'os'
10
- import axios from 'axios'
11
10
 
11
+ import { createClientFromForgeConf, type ExecutionRecord } from '@forgehive/hive-sdk'
12
12
  import { create as bundleCreate } from '../bundle/create'
13
13
  import { load as bundleLoad } from '../bundle/load'
14
14
  import { load as loadConf } from '../conf/load'
@@ -66,32 +66,47 @@ const boundaries = {
66
66
 
67
67
  return true
68
68
  },
69
- sendLogToAPI: async (profile: Profile, projectName: string, taskName: string, logItem: unknown, fixtureUUID: string): Promise<boolean> => {
69
+ sendLogToAPI: async (
70
+ profile: Profile,
71
+ record: ExecutionRecord,
72
+ fixtureUUID: string
73
+ ): Promise<{ success: boolean; logUuid?: string }> => {
70
74
  try {
71
- const logsUrl = `${profile.url}/api/tasks/log-ingest`
72
- const authToken = `${profile.apiKey}:${profile.apiSecret}`
73
-
74
- await axios.post(logsUrl, {
75
- projectName,
76
- taskName,
77
- logItem: JSON.stringify(logItem),
78
- replayFrom: fixtureUUID
79
- }, {
80
- headers: {
81
- Authorization: `Bearer ${authToken}`,
82
- 'Content-Type': 'application/json'
75
+ // Use createClientFromForgeConf to automatically load forge.json with task UUIDs
76
+ const client = createClientFromForgeConf('./forge.json', {
77
+ apiKey: profile.apiKey,
78
+ apiSecret: profile.apiSecret,
79
+ host: profile.url,
80
+ metadata: {
81
+ environment: 'cli',
82
+ replayFrom: fixtureUUID
83
83
  }
84
84
  })
85
85
 
86
86
  console.log('===============================================')
87
- console.log('Log sent to API... ', profile.name, profile.url)
88
- console.log('Replay from fixture UUID:', fixtureUUID)
87
+ console.log('Sending replay log to Hive...')
88
+ const result = await client.sendLog(record)
89
89
 
90
- return true
90
+ if (result === 'success' || (typeof result === 'object' && 'uuid' in result)) {
91
+ console.log('✅ Log sent to API...', profile.name, profile.url)
92
+ console.log('Replay from fixture UUID:', fixtureUUID)
93
+ console.log('===============================================')
94
+
95
+ if (typeof result === 'object' && result && 'uuid' in result) {
96
+ return { success: true, logUuid: result.uuid }
97
+ }
98
+
99
+ return { success: true }
100
+ } else {
101
+ console.error('❌ Failed to send log to Hive')
102
+ console.log('===============================================')
103
+ return { success: false }
104
+ }
91
105
  } catch (e) {
92
106
  const error = e as Error
93
- console.error('Failed to send log to API:', error.message)
94
- return false
107
+ console.error('Failed to send log to API:', error.message)
108
+ console.log('===============================================')
109
+ return { success: false }
95
110
  }
96
111
  }
97
112
  }
@@ -107,7 +122,6 @@ export const replay = createTask({
107
122
  // Load forge configuration
108
123
  const forge: ForgeConf = await loadConf({})
109
124
  const taskDescriptor = forge.tasks[descriptorName as keyof typeof forge.tasks]
110
- const projectName = forge.project.name
111
125
 
112
126
  if (taskDescriptor === undefined) {
113
127
  throw new Error(`Task ${descriptorName} is not defined in forge.json`)
@@ -203,7 +217,7 @@ export const replay = createTask({
203
217
  // Send the log to API if profile is available
204
218
  if (profile) {
205
219
  try {
206
- await sendLogToAPI(profile, projectName, descriptorName, record, fixture.fixtureUUID)
220
+ await sendLogToAPI(profile, record, fixture.fixtureUUID)
207
221
  } catch (e) {
208
222
  console.error('Failed to send log to API:', e)
209
223
  }
@@ -10,7 +10,7 @@ import os from 'os'
10
10
  import { createTask } from '@forgehive/task'
11
11
  import { Schema } from '@forgehive/schema'
12
12
  import { RecordTape } from '@forgehive/record-tape'
13
- import { HiveLogClient, type ExecutionRecord } from '@forgehive/hive-sdk'
13
+ import { createClientFromForgeConf, type ExecutionRecord } from '@forgehive/hive-sdk'
14
14
 
15
15
  import { create as bundleCreate } from '../bundle/create'
16
16
  import { load as bundleLoad } from '../bundle/load'
@@ -51,46 +51,37 @@ const boundaries = {
51
51
  },
52
52
  sendLogToAPI: async (
53
53
  profile: Profile,
54
- projectName: string,
55
54
  record: ExecutionRecord,
56
- taskUuid?: string,
57
- projectUuid?: string
55
+ taskUuid?: string
58
56
  ): Promise<{ success: boolean; logUuid?: string; taskUuid?: string; skipRemoteLog?: boolean }> => {
59
- // Check if we have required UUIDs for the new endpoint
60
- if (!projectUuid || !taskUuid) {
57
+ // Check if we have required UUID for the task
58
+ if (!taskUuid) {
61
59
  console.log('===============================================')
62
- console.log('⚠️ Remote logging skipped - missing UUIDs')
60
+ console.log('⚠️ Remote logging skipped - missing task UUID')
63
61
  console.log('')
64
62
  console.log('To enable remote logging with enhanced features:')
65
- if (!projectUuid) {
66
- console.log('• Use "forge project:create" to create a new project, or')
67
- console.log('• Use "forge project:link" to connect to an existing project')
68
- }
69
- if (!taskUuid) {
70
- console.log('• Use "forge project:sync" to get the task to have UUID')
71
- }
63
+ console.log('• Use "forge project:sync" to sync tasks and get UUIDs')
72
64
  console.log('===============================================')
73
65
  return { success: true, skipRemoteLog: true }
74
66
  }
75
67
 
76
68
  try {
77
- const config = {
78
- projectName,
79
- projectUuid,
69
+ // Use createClientFromForgeConf to automatically load forge.json with task UUIDs
70
+ const client = createClientFromForgeConf('./forge.json', {
80
71
  apiKey: profile.apiKey,
81
72
  apiSecret: profile.apiSecret,
82
73
  host: profile.url,
83
74
  metadata: {
84
75
  environment: 'cli'
85
76
  }
86
- }
77
+ })
87
78
 
88
- const client = new HiveLogClient(config)
79
+ console.log('===============================================')
89
80
  console.log('Sending execution log to Hive...')
90
- const result = await client.sendLogByUuid(record, taskUuid)
81
+ const result = await client.sendLog(record)
91
82
 
92
83
  if (result === 'success' || (typeof result === 'object' && 'uuid' in result)) {
93
- console.log('===============================================')
84
+ console.log('')
94
85
  console.log('✅ Log sent to Hive successfully')
95
86
  console.log(` Profile: ${profile.name}`)
96
87
  console.log(` Host: ${profile.url}`)
@@ -102,13 +93,17 @@ const boundaries = {
102
93
 
103
94
  return { success: true, taskUuid }
104
95
  } else {
96
+ console.log('')
105
97
  console.error('❌ Failed to send log to Hive:', profile.url)
98
+ console.log('===============================================')
106
99
  return { success: false }
107
100
  }
108
101
  } catch (e) {
102
+ console.log('===============================================')
109
103
  console.error('❌ Failed to send log to Hive:', profile.url)
110
104
  const error = e as Error
111
105
  console.error('Error:', error.message)
106
+ console.log('===============================================')
112
107
  return { success: false }
113
108
  }
114
109
  }
@@ -129,8 +124,6 @@ export const run = createTask({
129
124
  // Load forge configuration
130
125
  const forge: ForgeConf = await loadConf({})
131
126
  const taskDescriptor = forge.tasks[descriptorName as keyof typeof forge.tasks]
132
- const projectName = forge.project.name
133
- const projectUuid = forge.project.uuid
134
127
  const taskUuid = taskDescriptor?.uuid
135
128
 
136
129
  if (taskDescriptor === undefined) {
@@ -204,7 +197,7 @@ export const run = createTask({
204
197
 
205
198
  if (profile) {
206
199
  try {
207
- const logResult = await sendLogToAPI(profile, projectName, logItem, taskUuid, projectUuid)
200
+ const logResult = await sendLogToAPI(profile, logItem, taskUuid)
208
201
 
209
202
  if (logResult.success && !logResult.skipRemoteLog && taskUuid) {
210
203
  console.log(`🔗 View execution logs: ${profile.url}/tasks/${taskUuid}?tab=logs`)
@@ -22,6 +22,7 @@ export interface ForgeConf {
22
22
  project: {
23
23
  name: string
24
24
  uuid?: string
25
+ description?: string
25
26
  }
26
27
  paths: {
27
28
  logs: string