@forgehive/forge-cli 0.3.12 → 0.3.14

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
@@ -163,9 +163,10 @@ runner.setHandler(async (data) => {
163
163
  });
164
164
  }
165
165
  else if (taskName === 'docs:download') {
166
- const { path } = args;
166
+ const { path, logs } = args;
167
167
  result = await task.run({
168
- path
168
+ path,
169
+ logs
169
170
  });
170
171
  }
171
172
  else if (taskName === 'project:create') {
@@ -1,5 +1,6 @@
1
1
  export declare const download: import("@forgehive/task").TaskInstanceType<(argv: {
2
2
  path?: string | undefined;
3
+ logs?: boolean | undefined;
3
4
  }, boundaries: import("@forgehive/task").WrappedBoundaries<{
4
5
  fetchFile: (url: string) => Promise<string>;
5
6
  getCurrentWorkingDirectory: () => Promise<string>;
@@ -8,9 +9,13 @@ export declare const download: import("@forgehive/task").TaskInstanceType<(argv:
8
9
  checkFileExists: (filePath: string) => Promise<boolean>;
9
10
  }>) => Promise<{
10
11
  success: boolean;
11
- filePath: string;
12
- targetPath: string;
13
- size: number;
12
+ downloads: {
13
+ type: string;
14
+ filePath: string;
15
+ targetPath: string;
16
+ size: number;
17
+ }[];
18
+ totalFiles: number;
14
19
  }>, {
15
20
  fetchFile: (url: string) => Promise<string>;
16
21
  getCurrentWorkingDirectory: () => Promise<string>;
@@ -3,6 +3,8 @@
3
3
  // Run this task with:
4
4
  // forge task:run docs:download
5
5
  // forge task:run docs:download --path="custom/path/forge.md"
6
+ // forge task:run docs:download --logs
7
+ // forge task:run docs:download --logs --path="custom/path/forge.md"
6
8
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
9
  if (k2 === undefined) k2 = k;
8
10
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -45,10 +47,12 @@ const task_1 = require("@forgehive/task");
45
47
  const schema_1 = require("@forgehive/schema");
46
48
  const path_1 = __importDefault(require("path"));
47
49
  const name = 'docs:download';
48
- const description = 'Download the ForgeHive LLM guide from GitHub to local project';
50
+ const description = 'Download ForgeHive LLM guides from GitHub to local project';
49
51
  const LLM_GUIDE_URL = 'https://raw.githubusercontent.com/forge-and-hive/forge-mono-repo/refs/heads/main/docs/llm.md';
52
+ const LLM_HIVE_LOGGING_URL = 'https://raw.githubusercontent.com/forge-and-hive/forge-mono-repo/refs/heads/main/docs/llm-hive-logging.md';
50
53
  const schema = new schema_1.Schema({
51
- path: schema_1.Schema.string().optional()
54
+ path: schema_1.Schema.string().optional(),
55
+ logs: schema_1.Schema.boolean().optional()
52
56
  });
53
57
  const boundaries = {
54
58
  fetchFile: async (url) => {
@@ -85,30 +89,63 @@ exports.download = (0, task_1.createTask)({
85
89
  description,
86
90
  schema,
87
91
  boundaries,
88
- fn: async function ({ path: customPath }, { fetchFile, getCurrentWorkingDirectory, createDirectory, writeFile, checkFileExists }) {
89
- // Determine the target path
90
- const targetPath = customPath || 'docs/forge.md';
92
+ fn: async function ({ path: customPath, logs }, { fetchFile, getCurrentWorkingDirectory, createDirectory, writeFile, checkFileExists }) {
91
93
  const cwd = await getCurrentWorkingDirectory();
92
- const fullPath = path_1.default.resolve(cwd, targetPath);
93
- const dirPath = path_1.default.dirname(fullPath);
94
- console.log(`Downloading ForgeHive LLM guide to: ${targetPath}`);
94
+ const results = [];
95
+ // Download main LLM guide
96
+ const mainTargetPath = customPath || 'docs/forge.md';
97
+ const mainFullPath = path_1.default.resolve(cwd, mainTargetPath);
98
+ const mainDirPath = path_1.default.dirname(mainFullPath);
99
+ console.log(`Downloading ForgeHive LLM guide to: ${mainTargetPath}`);
95
100
  // Check if file already exists
96
- const fileExists = await checkFileExists(fullPath);
97
- if (fileExists) {
98
- console.log(`Warning: File already exists at ${targetPath}. It will be overwritten.`);
101
+ const mainFileExists = await checkFileExists(mainFullPath);
102
+ if (mainFileExists) {
103
+ console.log(`Warning: File already exists at ${mainTargetPath}. It will be overwritten.`);
99
104
  }
100
- // Download the file content
101
- const content = await fetchFile(LLM_GUIDE_URL);
105
+ // Download the main guide content
106
+ const mainContent = await fetchFile(LLM_GUIDE_URL);
102
107
  // Create directory if it doesn't exist
103
- await createDirectory(dirPath);
104
- // Write the file
105
- await writeFile(fullPath, content);
106
- console.log(`✅ Successfully downloaded ForgeHive LLM guide to ${targetPath}`);
108
+ await createDirectory(mainDirPath);
109
+ // Write the main guide file
110
+ await writeFile(mainFullPath, mainContent);
111
+ console.log(`✅ Successfully downloaded ForgeHive LLM guide to ${mainTargetPath}`);
112
+ results.push({
113
+ type: 'main',
114
+ filePath: mainFullPath,
115
+ targetPath: mainTargetPath,
116
+ size: mainContent.length
117
+ });
118
+ // Download Hive logging guide if --logs flag is provided
119
+ if (logs) {
120
+ const logsTargetPath = customPath
121
+ ? path_1.default.join(path_1.default.dirname(customPath), 'hive-logging.md')
122
+ : 'docs/hive-logging.md';
123
+ const logsFullPath = path_1.default.resolve(cwd, logsTargetPath);
124
+ const logsDirPath = path_1.default.dirname(logsFullPath);
125
+ console.log(`Downloading Hive Logging guide to: ${logsTargetPath}`);
126
+ // Check if logs file already exists
127
+ const logsFileExists = await checkFileExists(logsFullPath);
128
+ if (logsFileExists) {
129
+ console.log(`Warning: File already exists at ${logsTargetPath}. It will be overwritten.`);
130
+ }
131
+ // Download the logs guide content
132
+ const logsContent = await fetchFile(LLM_HIVE_LOGGING_URL);
133
+ // Create directory if it doesn't exist
134
+ await createDirectory(logsDirPath);
135
+ // Write the logs guide file
136
+ await writeFile(logsFullPath, logsContent);
137
+ console.log(`✅ Successfully downloaded Hive Logging guide to ${logsTargetPath}`);
138
+ results.push({
139
+ type: 'logs',
140
+ filePath: logsFullPath,
141
+ targetPath: logsTargetPath,
142
+ size: logsContent.length
143
+ });
144
+ }
107
145
  return {
108
146
  success: true,
109
- filePath: fullPath,
110
- targetPath,
111
- size: content.length
147
+ downloads: results,
148
+ totalFiles: results.length
112
149
  };
113
150
  }
114
151
  });
@@ -17,10 +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, taskUuid?: string) => Promise<{
20
+ sendLogToAPI: (profile: Profile, projectName: string, record: ExecutionRecord, taskUuid?: string, projectUuid?: string) => Promise<{
21
21
  success: boolean;
22
22
  logUuid?: string;
23
23
  taskUuid?: string;
24
+ skipRemoteLog?: boolean;
24
25
  }>;
25
26
  }>) => Promise<any>, {
26
27
  loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
@@ -36,9 +37,10 @@ export declare const run: import("@forgehive/task").TaskInstanceType<(argv: {
36
37
  }) => Promise<Promise<any>>;
37
38
  ensureLogFolder: (logsPath: string) => Promise<void>;
38
39
  ensureBuildsFolder: () => Promise<string>;
39
- sendLogToAPI: (profile: Profile, projectName: string, record: ExecutionRecord, taskUuid?: string) => Promise<{
40
+ sendLogToAPI: (profile: Profile, projectName: string, record: ExecutionRecord, taskUuid?: string, projectUuid?: string) => Promise<{
40
41
  success: boolean;
41
42
  logUuid?: string;
42
43
  taskUuid?: string;
44
+ skipRemoteLog?: boolean;
43
45
  }>;
44
46
  }>;
@@ -50,10 +50,27 @@ const boundaries = {
50
50
  }
51
51
  return buildsPath;
52
52
  },
53
- sendLogToAPI: async (profile, projectName, record, taskUuid) => {
53
+ sendLogToAPI: async (profile, projectName, record, taskUuid, projectUuid) => {
54
+ // Check if we have required UUIDs for the new endpoint
55
+ if (!projectUuid || !taskUuid) {
56
+ console.log('===============================================');
57
+ console.log('⚠️ Remote logging skipped - missing UUIDs');
58
+ console.log('');
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
+ }
67
+ console.log('===============================================');
68
+ return { success: true, skipRemoteLog: true };
69
+ }
54
70
  try {
55
71
  const config = {
56
72
  projectName,
73
+ projectUuid,
57
74
  apiKey: profile.apiKey,
58
75
  apiSecret: profile.apiSecret,
59
76
  host: profile.url,
@@ -62,10 +79,13 @@ const boundaries = {
62
79
  }
63
80
  };
64
81
  const client = new hive_sdk_1.HiveLogClient(config);
65
- const result = await client.sendLog(record);
82
+ console.log('Sending execution log to Hive...');
83
+ const result = await client.sendLogByUuid(record, taskUuid);
66
84
  if (result === 'success' || (typeof result === 'object' && 'uuid' in result)) {
67
85
  console.log('===============================================');
68
- console.log('Log sent to API... ', profile.name, profile.url);
86
+ console.log('Log sent to Hive successfully');
87
+ console.log(` Profile: ${profile.name}`);
88
+ console.log(` Host: ${profile.url}`);
69
89
  if (typeof result === 'object' && result && 'uuid' in result) {
70
90
  const logResponse = result;
71
91
  return { success: true, logUuid: logResponse.uuid, taskUuid };
@@ -73,12 +93,12 @@ const boundaries = {
73
93
  return { success: true, taskUuid };
74
94
  }
75
95
  else {
76
- console.error('Failed to send log to API:', profile.url);
96
+ console.error('Failed to send log to Hive:', profile.url);
77
97
  return { success: false };
78
98
  }
79
99
  }
80
100
  catch (e) {
81
- console.error('Failed to send log to API:', profile.url);
101
+ console.error('Failed to send log to Hive:', profile.url);
82
102
  const error = e;
83
103
  console.error('Error:', error.message);
84
104
  return { success: false };
@@ -93,6 +113,7 @@ exports.run = (0, task_1.createTask)({
93
113
  const forge = await loadConf({});
94
114
  const taskDescriptor = forge.tasks[descriptorName];
95
115
  const projectName = forge.project.name;
116
+ const projectUuid = forge.project.uuid;
96
117
  const taskUuid = taskDescriptor?.uuid;
97
118
  if (taskDescriptor === undefined) {
98
119
  throw new Error('Task is not defined on forge.json');
@@ -154,8 +175,8 @@ exports.run = (0, task_1.createTask)({
154
175
  await tape.save();
155
176
  if (profile) {
156
177
  try {
157
- const logResult = await sendLogToAPI(profile, projectName, logItem, taskUuid);
158
- if (logResult.success && taskUuid) {
178
+ const logResult = await sendLogToAPI(profile, projectName, logItem, taskUuid, projectUuid);
179
+ if (logResult.success && !logResult.skipRemoteLog && taskUuid) {
159
180
  console.log(`🔗 View execution logs: ${profile.url}/tasks/${taskUuid}?tab=logs`);
160
181
  }
161
182
  }
@@ -0,0 +1 @@
1
+ {"input":{},"boundaries":{"loadConf":[{"input":[{}],"output":{"project":{"name":"forge-cli"},"paths":{"logs":"logs/","fixtures":"fixtures","fingerprints":"fingerprints/","tasks":"src/tasks/","runners":"src/runners/","tests":"src/tests/"},"infra":{"region":"us-west-2","bucket":""},"tasks":{"task:createTask":{"path":"src/tasks/task/createTask.ts","handler":"createTask"},"bundle:create":{"path":"src/tasks/bundle/create.ts","handler":"create"},"bundle:load":{"path":"src/tasks/bundle/load.ts","handler":"load"},"task:run":{"path":"src/tasks/task/run.ts","handler":"run"},"task:remove":{"path":"src/tasks/task/remove.ts","handler":"remove"},"conf:info":{"path":"src/tasks/conf/info.ts","handler":"info"},"runner:create":{"path":"src/tasks/runner/create.ts","handler":"create"},"runner:remove":{"path":"src/tasks/runner/remove.ts","handler":"remove"},"runner:bundle":{"path":"src/tasks/runner/bundle.ts","handler":"bundle"},"task:publish":{"path":"src/tasks/task/publish.ts","handler":"publish"},"task:download":{"path":"src/tasks/task/download.ts","handler":"download"},"auth:add":{"path":"src/tasks/auth/add.ts","handler":"add"},"auth:load":{"path":"src/tasks/auth/load.ts","handler":"load"},"auth:loadCurrent":{"path":"src/tasks/auth/loadCurrent.ts","handler":"loadCurrent"},"auth:switch":{"path":"src/tasks/auth/switch.ts","handler":"switchProfile"},"auth:list":{"path":"src/tasks/auth/list.ts","handler":"list"},"auth:remove":{"path":"src/tasks/auth/remove.ts","handler":"remove"},"task:replay":{"path":"src/tasks/task/replay.ts","handler":"replay"},"fixture:download":{"path":"src/tasks/fixture/download.ts","handler":"download"},"bundle:zip":{"path":"src/tasks/bundle/zip.ts","handler":"zip"},"task:list":{"path":"src/tasks/task/list.ts","handler":"list"},"task:describe":{"path":"src/tasks/task/describe.ts","handler":"describe"},"task:fingerprint":{"path":"src/tasks/task/fingerprint.ts","handler":"fingerprint"},"bundle:fingerprint":{"path":"src/tasks/bundle/fingerprint.ts","handler":"fingerprint"},"task:invoke":{"path":"src/tasks/task/invoke.ts","handler":"invoke"},"docs:download":{"path":"src/tasks/docs/download.ts","handler":"download"},"project:create":{"path":"src/tasks/project/create.ts","handler":"create"},"project:link":{"path":"src/tasks/project/link.ts","handler":"link","uuid":"cb9f82e1-d397-46d9-9f0d-2b0e3becbfa1"},"project:unlink":{"path":"src/tasks/project/unlink.ts","handler":"unlink","uuid":"414d37de-793c-4d01-899d-69515f5e0948"}},"runners":{}},"timing":{"startTime":1755627445111,"endTime":1755627445112,"duration":1}}]},"metadata":{"environment":"cli"},"metrics":[],"type":"success","output":{"taskCount":29},"timing":{"startTime":1755627445111,"endTime":1755627445112,"duration":1}}
@@ -0,0 +1 @@
1
+ {"input":{},"boundaries":{},"taskName":"test:guidance","metadata":{"environment":"cli"},"metrics":[],"type":"success","output":{},"timing":{"startTime":1755627427045,"endTime":1755627427045,"duration":0}}
@@ -0,0 +1 @@
1
+ {"uuid":"0198bfeb-69c0-77e8-882b-a76dcb9300f9","input":{},"boundaries":{},"taskName":"test:uuid","metadata":{"environment":"cli"},"metrics":[],"type":"success","output":{},"timing":{"startTime":1755566533056,"endTime":1755566533057,"duration":1}}
@@ -0,0 +1 @@
1
+ {"input":{},"boundaries":{},"taskName":"test:uuidCheck","metadata":{"environment":"cli"},"metrics":[],"type":"success","output":{},"timing":{"startTime":1755567492456,"endTime":1755567492456,"duration":0}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgehive/forge-cli",
3
- "version": "0.3.12",
3
+ "version": "0.3.14",
4
4
  "description": "TypeScript CLI application",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -10,11 +10,11 @@
10
10
  "publishConfig": {
11
11
  "access": "public",
12
12
  "dependencies": {
13
- "@forgehive/hive-sdk": "^0.1.3",
14
- "@forgehive/record-tape": "^0.2.5",
15
- "@forgehive/runner": "^0.2.5",
13
+ "@forgehive/hive-sdk": "^0.1.4",
14
+ "@forgehive/record-tape": "^0.2.6",
15
+ "@forgehive/runner": "^0.2.6",
16
16
  "@forgehive/schema": "^0.1.4",
17
- "@forgehive/task": "^0.2.5",
17
+ "@forgehive/task": "^0.2.6",
18
18
  "esbuild": "^0.25.0",
19
19
  "handlebars": "^4.7.8",
20
20
  "minimist": "^1.2.8",
@@ -30,11 +30,11 @@
30
30
  "minimist": "^1.2.8",
31
31
  "typescript": "^5.3.3",
32
32
  "uuid": "^11.1.0",
33
- "@forgehive/hive-sdk": "0.1.3",
34
- "@forgehive/record-tape": "0.2.5",
35
- "@forgehive/runner": "0.2.5",
33
+ "@forgehive/record-tape": "0.2.6",
34
+ "@forgehive/hive-sdk": "0.1.4",
36
35
  "@forgehive/schema": "0.1.4",
37
- "@forgehive/task": "0.2.5"
36
+ "@forgehive/runner": "0.2.6",
37
+ "@forgehive/task": "0.2.6"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/archiver": "^6.0.3",
package/src/runner.ts CHANGED
@@ -184,10 +184,11 @@ runner.setHandler(async (data: ParsedArgs): Promise<unknown> => {
184
184
  profileName: String(action)
185
185
  })
186
186
  } else if (taskName === 'docs:download') {
187
- const { path } = args as { path?: string }
187
+ const { path, logs } = args as { path?: string, logs?: boolean }
188
188
 
189
189
  result = await task.run({
190
- path
190
+ path,
191
+ logs
191
192
  })
192
193
  } else if (taskName === 'project:create') {
193
194
  const { projectName, description } = args as { projectName?: string, description?: string }
@@ -2,18 +2,22 @@
2
2
  // Run this task with:
3
3
  // forge task:run docs:download
4
4
  // forge task:run docs:download --path="custom/path/forge.md"
5
+ // forge task:run docs:download --logs
6
+ // forge task:run docs:download --logs --path="custom/path/forge.md"
5
7
 
6
8
  import { createTask } from '@forgehive/task'
7
9
  import { Schema } from '@forgehive/schema'
8
10
  import path from 'path'
9
11
 
10
12
  const name = 'docs:download'
11
- const description = 'Download the ForgeHive LLM guide from GitHub to local project'
13
+ const description = 'Download ForgeHive LLM guides from GitHub to local project'
12
14
 
13
15
  const LLM_GUIDE_URL = 'https://raw.githubusercontent.com/forge-and-hive/forge-mono-repo/refs/heads/main/docs/llm.md'
16
+ const LLM_HIVE_LOGGING_URL = 'https://raw.githubusercontent.com/forge-and-hive/forge-mono-repo/refs/heads/main/docs/llm-hive-logging.md'
14
17
 
15
18
  const schema = new Schema({
16
- path: Schema.string().optional()
19
+ path: Schema.string().optional(),
20
+ logs: Schema.boolean().optional()
17
21
  })
18
22
 
19
23
  const boundaries = {
@@ -51,37 +55,80 @@ export const download = createTask({
51
55
  description,
52
56
  schema,
53
57
  boundaries,
54
- fn: async function ({ path: customPath }, { fetchFile, getCurrentWorkingDirectory, createDirectory, writeFile, checkFileExists }) {
55
- // Determine the target path
56
- const targetPath = customPath || 'docs/forge.md'
58
+ fn: async function ({ path: customPath, logs }, { fetchFile, getCurrentWorkingDirectory, createDirectory, writeFile, checkFileExists }) {
57
59
  const cwd = await getCurrentWorkingDirectory()
58
- const fullPath = path.resolve(cwd, targetPath)
59
- const dirPath = path.dirname(fullPath)
60
+ const results = []
60
61
 
61
- console.log(`Downloading ForgeHive LLM guide to: ${targetPath}`)
62
+ // Download main LLM guide
63
+ const mainTargetPath = customPath || 'docs/forge.md'
64
+ const mainFullPath = path.resolve(cwd, mainTargetPath)
65
+ const mainDirPath = path.dirname(mainFullPath)
66
+
67
+ console.log(`Downloading ForgeHive LLM guide to: ${mainTargetPath}`)
62
68
 
63
69
  // Check if file already exists
64
- const fileExists = await checkFileExists(fullPath)
65
- if (fileExists) {
66
- console.log(`Warning: File already exists at ${targetPath}. It will be overwritten.`)
70
+ const mainFileExists = await checkFileExists(mainFullPath)
71
+ if (mainFileExists) {
72
+ console.log(`Warning: File already exists at ${mainTargetPath}. It will be overwritten.`)
67
73
  }
68
74
 
69
- // Download the file content
70
- const content = await fetchFile(LLM_GUIDE_URL)
75
+ // Download the main guide content
76
+ const mainContent = await fetchFile(LLM_GUIDE_URL)
71
77
 
72
78
  // Create directory if it doesn't exist
73
- await createDirectory(dirPath)
79
+ await createDirectory(mainDirPath)
80
+
81
+ // Write the main guide file
82
+ await writeFile(mainFullPath, mainContent)
83
+
84
+ console.log(`✅ Successfully downloaded ForgeHive LLM guide to ${mainTargetPath}`)
85
+
86
+ results.push({
87
+ type: 'main',
88
+ filePath: mainFullPath,
89
+ targetPath: mainTargetPath,
90
+ size: mainContent.length
91
+ })
92
+
93
+ // Download Hive logging guide if --logs flag is provided
94
+ if (logs) {
95
+ const logsTargetPath = customPath
96
+ ? path.join(path.dirname(customPath), 'hive-logging.md')
97
+ : 'docs/hive-logging.md'
98
+ const logsFullPath = path.resolve(cwd, logsTargetPath)
99
+ const logsDirPath = path.dirname(logsFullPath)
100
+
101
+ console.log(`Downloading Hive Logging guide to: ${logsTargetPath}`)
74
102
 
75
- // Write the file
76
- await writeFile(fullPath, content)
103
+ // Check if logs file already exists
104
+ const logsFileExists = await checkFileExists(logsFullPath)
105
+ if (logsFileExists) {
106
+ console.log(`Warning: File already exists at ${logsTargetPath}. It will be overwritten.`)
107
+ }
77
108
 
78
- console.log(`✅ Successfully downloaded ForgeHive LLM guide to ${targetPath}`)
109
+ // Download the logs guide content
110
+ const logsContent = await fetchFile(LLM_HIVE_LOGGING_URL)
111
+
112
+ // Create directory if it doesn't exist
113
+ await createDirectory(logsDirPath)
114
+
115
+ // Write the logs guide file
116
+ await writeFile(logsFullPath, logsContent)
117
+
118
+ console.log(`✅ Successfully downloaded Hive Logging guide to ${logsTargetPath}`)
119
+
120
+ results.push({
121
+ type: 'logs',
122
+ filePath: logsFullPath,
123
+ targetPath: logsTargetPath,
124
+ size: logsContent.length
125
+ })
126
+ }
79
127
 
80
128
  return {
81
129
  success: true,
82
- filePath: fullPath,
83
- targetPath,
84
- size: content.length
130
+ downloads: results,
131
+ totalFiles: results.length
85
132
  }
86
133
  }
87
134
  })
@@ -49,10 +49,34 @@ const boundaries = {
49
49
 
50
50
  return buildsPath
51
51
  },
52
- sendLogToAPI: async (profile: Profile, projectName: string, record: ExecutionRecord, taskUuid?: string): Promise<{ success: boolean; logUuid?: string; taskUuid?: string }> => {
52
+ sendLogToAPI: async (
53
+ profile: Profile,
54
+ projectName: string,
55
+ record: ExecutionRecord,
56
+ taskUuid?: string,
57
+ projectUuid?: string
58
+ ): Promise<{ success: boolean; logUuid?: string; taskUuid?: string; skipRemoteLog?: boolean }> => {
59
+ // Check if we have required UUIDs for the new endpoint
60
+ if (!projectUuid || !taskUuid) {
61
+ console.log('===============================================')
62
+ console.log('⚠️ Remote logging skipped - missing UUIDs')
63
+ console.log('')
64
+ 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
+ }
72
+ console.log('===============================================')
73
+ return { success: true, skipRemoteLog: true }
74
+ }
75
+
53
76
  try {
54
77
  const config = {
55
78
  projectName,
79
+ projectUuid,
56
80
  apiKey: profile.apiKey,
57
81
  apiSecret: profile.apiSecret,
58
82
  host: profile.url,
@@ -62,11 +86,14 @@ const boundaries = {
62
86
  }
63
87
 
64
88
  const client = new HiveLogClient(config)
65
- const result = await client.sendLog(record)
89
+ console.log('Sending execution log to Hive...')
90
+ const result = await client.sendLogByUuid(record, taskUuid)
66
91
 
67
92
  if (result === 'success' || (typeof result === 'object' && 'uuid' in result)) {
68
93
  console.log('===============================================')
69
- console.log('Log sent to API... ', profile.name, profile.url)
94
+ console.log('Log sent to Hive successfully')
95
+ console.log(` Profile: ${profile.name}`)
96
+ console.log(` Host: ${profile.url}`)
70
97
 
71
98
  if (typeof result === 'object' && result && 'uuid' in result) {
72
99
  const logResponse = result as { uuid: string }
@@ -75,11 +102,11 @@ const boundaries = {
75
102
 
76
103
  return { success: true, taskUuid }
77
104
  } else {
78
- console.error('Failed to send log to API:', profile.url)
105
+ console.error('Failed to send log to Hive:', profile.url)
79
106
  return { success: false }
80
107
  }
81
108
  } catch (e) {
82
- console.error('Failed to send log to API:', profile.url)
109
+ console.error('Failed to send log to Hive:', profile.url)
83
110
  const error = e as Error
84
111
  console.error('Error:', error.message)
85
112
  return { success: false }
@@ -103,6 +130,7 @@ export const run = createTask({
103
130
  const forge: ForgeConf = await loadConf({})
104
131
  const taskDescriptor = forge.tasks[descriptorName as keyof typeof forge.tasks]
105
132
  const projectName = forge.project.name
133
+ const projectUuid = forge.project.uuid
106
134
  const taskUuid = taskDescriptor?.uuid
107
135
 
108
136
  if (taskDescriptor === undefined) {
@@ -176,9 +204,9 @@ export const run = createTask({
176
204
 
177
205
  if (profile) {
178
206
  try {
179
- const logResult = await sendLogToAPI(profile, projectName, logItem, taskUuid)
207
+ const logResult = await sendLogToAPI(profile, projectName, logItem, taskUuid, projectUuid)
180
208
 
181
- if (logResult.success && taskUuid) {
209
+ if (logResult.success && !logResult.skipRemoteLog && taskUuid) {
182
210
  console.log(`🔗 View execution logs: ${profile.url}/tasks/${taskUuid}?tab=logs`)
183
211
  }
184
212
  } catch (e) {