@forgehive/forge-cli 0.2.4 → 0.2.5

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.
@@ -27,7 +27,7 @@ exports.init = (0, task_1.createTask)(schema, boundaries, async function (argv,
27
27
  const forgePath = path_1.default.join(cwd, 'forge.json');
28
28
  const config = {
29
29
  project: {
30
- name: 'ChangeMePls'
30
+ name: 'BaseProject'
31
31
  },
32
32
  paths: {
33
33
  logs: 'logs/',
@@ -101,6 +101,7 @@ exports.publish = (0, task_1.createTask)(schema, boundaries, async function ({ d
101
101
  const task = bundle[taskDescriptor.handler];
102
102
  const description = task.getDescription() ?? '';
103
103
  const schema = task.getSchema() || new schema_1.Schema({});
104
+ const boundaries = Object.keys(task.getBoundaries()) || [];
104
105
  const schemaDescriptor = schema.describe();
105
106
  // Read the task file content
106
107
  const sourceCode = await readFileUtf8(entryPoint);
@@ -114,6 +115,7 @@ exports.publish = (0, task_1.createTask)(schema, boundaries, async function ({ d
114
115
  projectName,
115
116
  description,
116
117
  schemaDescriptor: JSON.stringify(schemaDescriptor),
118
+ boundaries,
117
119
  sourceCode,
118
120
  bundleSize
119
121
  };
@@ -1,9 +1,10 @@
1
- import { type ForgeConf } from '../types';
1
+ import { type ForgeConf, type Profile } from '../types';
2
2
  export declare const run: import("@forgehive/task").TaskInstanceType<(argv: {
3
3
  descriptorName: string;
4
4
  args: Record<string, string | number | boolean>;
5
5
  }, boundaries: import("@forgehive/task").WrappedBoundaries<{
6
6
  loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
7
+ loadCurrentProfile: (args: {}) => Promise<Promise<Profile>>;
7
8
  bundleCreate: (args: {
8
9
  entryPoint: string;
9
10
  outputFile: string;
@@ -15,8 +16,10 @@ export declare const run: import("@forgehive/task").TaskInstanceType<(argv: {
15
16
  }) => Promise<Promise<any>>;
16
17
  verifyLogFolder: (logsPath: string) => Promise<boolean>;
17
18
  ensureBuildsFolder: () => Promise<string>;
19
+ sendLogToAPI: (profile: Profile, projectName: string, taskName: string, logItem: unknown) => Promise<boolean>;
18
20
  }>) => Promise<any>, {
19
21
  loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
22
+ loadCurrentProfile: (args: {}) => Promise<Promise<Profile>>;
20
23
  bundleCreate: (args: {
21
24
  entryPoint: string;
22
25
  outputFile: string;
@@ -28,4 +31,5 @@ export declare const run: import("@forgehive/task").TaskInstanceType<(argv: {
28
31
  }) => Promise<Promise<any>>;
29
32
  verifyLogFolder: (logsPath: string) => Promise<boolean>;
30
33
  ensureBuildsFolder: () => Promise<string>;
34
+ sendLogToAPI: (profile: Profile, projectName: string, taskName: string, logItem: unknown) => Promise<boolean>;
31
35
  }>;
@@ -10,12 +10,14 @@ exports.run = void 0;
10
10
  const path_1 = __importDefault(require("path"));
11
11
  const promises_1 = __importDefault(require("fs/promises"));
12
12
  const os_1 = __importDefault(require("os"));
13
+ const axios_1 = __importDefault(require("axios"));
13
14
  const task_1 = require("@forgehive/task");
14
15
  const schema_1 = require("@forgehive/schema");
15
16
  const record_tape_1 = require("@forgehive/record-tape");
16
17
  const create_1 = require("../bundle/create");
17
18
  const load_1 = require("../bundle/load");
18
19
  const load_2 = require("../conf/load");
20
+ const loadCurrent_1 = require("../auth/loadCurrent");
19
21
  // For now, we'll use a simple schema without the record type
20
22
  // TODO: Use Schema.record once it's properly built and available
21
23
  const schema = new schema_1.Schema({
@@ -25,6 +27,7 @@ const schema = new schema_1.Schema({
25
27
  });
26
28
  const boundaries = {
27
29
  loadConf: load_2.load.asBoundary(),
30
+ loadCurrentProfile: loadCurrent_1.loadCurrent.asBoundary(),
28
31
  bundleCreate: create_1.create.asBoundary(),
29
32
  bundleLoad: load_1.load.asBoundary(),
30
33
  verifyLogFolder: async (logsPath) => {
@@ -46,15 +49,49 @@ const boundaries = {
46
49
  await promises_1.default.mkdir(buildsPath, { recursive: true });
47
50
  }
48
51
  return buildsPath;
52
+ },
53
+ sendLogToAPI: async (profile, projectName, taskName, logItem) => {
54
+ try {
55
+ const logsUrl = `${profile.url}/api/tasks/log-ingest`;
56
+ const authToken = `${profile.apiKey}:${profile.apiSecret}`;
57
+ await axios_1.default.post(logsUrl, {
58
+ projectName,
59
+ taskName,
60
+ logItem: JSON.stringify(logItem)
61
+ }, {
62
+ headers: {
63
+ Authorization: `Bearer ${authToken}`,
64
+ 'Content-Type': 'application/json'
65
+ }
66
+ });
67
+ console.log('===============================================');
68
+ console.log('Log sent to API... ', profile.name, profile.url);
69
+ return true;
70
+ }
71
+ catch (e) {
72
+ const error = e;
73
+ console.error('Failed to send log to API:', error.message);
74
+ return false;
75
+ }
49
76
  }
50
77
  };
51
- exports.run = (0, task_1.createTask)(schema, boundaries, async function ({ descriptorName, args }, { loadConf, bundleCreate, bundleLoad, verifyLogFolder, ensureBuildsFolder }) {
78
+ exports.run = (0, task_1.createTask)(schema, boundaries, async function ({ descriptorName, args }, { loadConf, bundleCreate, bundleLoad, verifyLogFolder, ensureBuildsFolder, loadCurrentProfile, sendLogToAPI }) {
52
79
  // Load forge configuration
53
80
  const forge = await loadConf({});
54
81
  const taskDescriptor = forge.tasks[descriptorName];
82
+ const projectName = forge.project.name;
55
83
  if (taskDescriptor === undefined) {
56
84
  throw new Error('Task is not defined on forge.json');
57
85
  }
86
+ // Try to load profile, but continue if not found
87
+ let profile = null;
88
+ try {
89
+ profile = await loadCurrentProfile({});
90
+ }
91
+ catch (error) {
92
+ // Profile not found or not configured, continue without it
93
+ console.log('No profile found, logs will not be sent to remote API');
94
+ }
58
95
  // Verify if log folder exists
59
96
  const logFolderPath = path_1.default.join(process.cwd(), forge.paths.logs);
60
97
  const logFolderExists = await verifyLogFolder(logFolderPath);
@@ -105,14 +142,26 @@ exports.run = (0, task_1.createTask)(schema, boundaries, async function ({ descr
105
142
  }
106
143
  tape.recordFrom(descriptorName, task);
107
144
  // Run the task with provided arguments
108
- let result;
145
+ let result, error;
109
146
  try {
110
147
  result = await task.run(args);
111
148
  }
112
- catch (error) {
113
- await tape.save();
114
- throw error;
149
+ catch (e) {
150
+ error = e;
115
151
  }
116
152
  await tape.save();
153
+ const logItems = tape.getLog();
154
+ const lastLogItem = logItems[logItems.length - 1];
155
+ if (profile) {
156
+ try {
157
+ await sendLogToAPI(profile, projectName, descriptorName, lastLogItem);
158
+ }
159
+ catch (e) {
160
+ console.error('Failed to send log to API:', e);
161
+ }
162
+ }
163
+ if (error) {
164
+ throw error;
165
+ }
117
166
  return result;
118
167
  });
@@ -43,7 +43,7 @@ describe('Init task', () => {
43
43
  const fileContent = await fs.promises.readFile(path_1.default.join(rootDir, 'forge.json'), 'utf-8');
44
44
  const config = JSON.parse(fileContent);
45
45
  // Verify the file content
46
- expect(config).toHaveProperty('project.name', 'ChangeMePls');
46
+ expect(config).toHaveProperty('project.name', 'BaseProject');
47
47
  expect(config).toHaveProperty('paths.logs', 'logs/');
48
48
  expect(config).toHaveProperty('paths.tasks', 'src/tasks/');
49
49
  expect(config).toHaveProperty('infra.region', 'us-west-2');
@@ -66,7 +66,7 @@ describe('Init task', () => {
66
66
  // Verify saveFile was not called
67
67
  expect(saveFileFn).not.toHaveBeenCalled();
68
68
  // Verify the returned config has the correct structure
69
- expect(result).toHaveProperty('project.name', 'ChangeMePls');
69
+ expect(result).toHaveProperty('project.name', 'BaseProject');
70
70
  expect(result).toHaveProperty('paths.logs', 'logs/');
71
71
  expect(result).toHaveProperty('paths.tasks', 'src/tasks/');
72
72
  expect(result).toHaveProperty('infra.region', 'us-west-2');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgehive/forge-cli",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "TypeScript CLI application",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -26,9 +26,9 @@
26
26
  "handlebars": "^4.7.8",
27
27
  "minimist": "^1.2.8",
28
28
  "@forgehive/task": "0.1.5",
29
+ "@forgehive/schema": "0.1.4",
29
30
  "@forgehive/record-tape": "0.0.2",
30
- "@forgehive/runner": "0.1.5",
31
- "@forgehive/schema": "0.1.4"
31
+ "@forgehive/runner": "0.1.5"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/jest": "^29.5.3",
package/src/tasks/init.ts CHANGED
@@ -32,7 +32,7 @@ export const init = createTask(
32
32
 
33
33
  const config: ForgeConf = {
34
34
  project: {
35
- name: 'ChangeMePls'
35
+ name: 'BaseProject'
36
36
  },
37
37
  paths: {
38
38
  logs: 'logs/',
@@ -126,6 +126,7 @@ export const publish = createTask(
126
126
  const task = bundle[taskDescriptor.handler]
127
127
  const description = task.getDescription() ?? ''
128
128
  const schema = task.getSchema() || new Schema({})
129
+ const boundaries = Object.keys(task.getBoundaries()) || []
129
130
  const schemaDescriptor = schema.describe()
130
131
 
131
132
  // Read the task file content
@@ -142,6 +143,7 @@ export const publish = createTask(
142
143
  projectName,
143
144
  description,
144
145
  schemaDescriptor: JSON.stringify(schemaDescriptor),
146
+ boundaries,
145
147
  sourceCode,
146
148
  bundleSize
147
149
  }
@@ -5,6 +5,7 @@
5
5
  import path from 'path'
6
6
  import fs from 'fs/promises'
7
7
  import os from 'os'
8
+ import axios from 'axios'
8
9
 
9
10
  import { createTask } from '@forgehive/task'
10
11
  import { Schema } from '@forgehive/schema'
@@ -13,7 +14,8 @@ import { RecordTape } from '@forgehive/record-tape'
13
14
  import { create as bundleCreate } from '../bundle/create'
14
15
  import { load as bundleLoad } from '../bundle/load'
15
16
  import { load as loadConf } from '../conf/load'
16
- import { type ForgeConf } from '../types'
17
+ import { loadCurrent as loadCurrentProfile } from '../auth/loadCurrent'
18
+ import { type ForgeConf, type Profile } from '../types'
17
19
 
18
20
  // For now, we'll use a simple schema without the record type
19
21
  // TODO: Use Schema.record once it's properly built and available
@@ -25,6 +27,7 @@ const schema = new Schema({
25
27
 
26
28
  const boundaries = {
27
29
  loadConf: loadConf.asBoundary(),
30
+ loadCurrentProfile: loadCurrentProfile.asBoundary(),
28
31
  bundleCreate: bundleCreate.asBoundary(),
29
32
  bundleLoad: bundleLoad.asBoundary(),
30
33
  verifyLogFolder: async (logsPath: string): Promise<boolean> => {
@@ -46,21 +49,65 @@ const boundaries = {
46
49
  }
47
50
 
48
51
  return buildsPath
52
+ },
53
+ sendLogToAPI: async (profile: Profile, projectName: string, taskName: string, logItem: unknown): Promise<boolean> => {
54
+ try {
55
+ const logsUrl = `${profile.url}/api/tasks/log-ingest`
56
+ const authToken = `${profile.apiKey}:${profile.apiSecret}`
57
+
58
+ await axios.post(logsUrl, {
59
+ projectName,
60
+ taskName,
61
+ logItem: JSON.stringify(logItem)
62
+ }, {
63
+ headers: {
64
+ Authorization: `Bearer ${authToken}`,
65
+ 'Content-Type': 'application/json'
66
+ }
67
+ })
68
+
69
+ console.log('===============================================')
70
+ console.log('Log sent to API... ', profile.name, profile.url)
71
+
72
+ return true
73
+ } catch (e) {
74
+ const error = e as Error
75
+ console.error('Failed to send log to API:', error.message)
76
+ return false
77
+ }
49
78
  }
50
79
  }
51
80
 
52
81
  export const run = createTask(
53
82
  schema,
54
83
  boundaries,
55
- async function ({ descriptorName, args }, { loadConf, bundleCreate, bundleLoad, verifyLogFolder, ensureBuildsFolder }) {
84
+ async function ({ descriptorName, args }, {
85
+ loadConf,
86
+ bundleCreate,
87
+ bundleLoad,
88
+ verifyLogFolder,
89
+ ensureBuildsFolder,
90
+ loadCurrentProfile,
91
+ sendLogToAPI
92
+ }) {
56
93
  // Load forge configuration
57
94
  const forge: ForgeConf = await loadConf({})
58
95
  const taskDescriptor = forge.tasks[descriptorName as keyof typeof forge.tasks]
96
+ const projectName = forge.project.name
59
97
 
60
98
  if (taskDescriptor === undefined) {
61
99
  throw new Error('Task is not defined on forge.json')
62
100
  }
63
101
 
102
+ // Try to load profile, but continue if not found
103
+ let profile = null
104
+ try {
105
+ profile = await loadCurrentProfile({})
106
+ } catch (error) {
107
+ // Profile not found or not configured, continue without it
108
+ console.log('No profile found, logs will not be sent to remote API')
109
+ }
110
+
64
111
  // Verify if log folder exists
65
112
  const logFolderPath = path.join(process.cwd(), forge.paths.logs)
66
113
  const logFolderExists = await verifyLogFolder(logFolderPath)
@@ -122,15 +169,28 @@ export const run = createTask(
122
169
  tape.recordFrom(descriptorName, task)
123
170
 
124
171
  // Run the task with provided arguments
125
- let result
172
+ let result, error
126
173
  try {
127
174
  result = await task.run(args)
128
- } catch (error) {
129
- await tape.save()
130
- throw error
175
+ } catch (e) {
176
+ error = e as Error
131
177
  }
132
178
 
133
179
  await tape.save()
180
+ const logItems = tape.getLog()
181
+ const lastLogItem = logItems[logItems.length - 1]
182
+
183
+ if (profile) {
184
+ try {
185
+ await sendLogToAPI(profile, projectName, descriptorName, lastLogItem)
186
+ } catch (e) {
187
+ console.error('Failed to send log to API:', e)
188
+ }
189
+ }
190
+
191
+ if (error) {
192
+ throw error
193
+ }
134
194
 
135
195
  return result
136
196
  }
@@ -48,7 +48,7 @@ describe('Init task', () => {
48
48
  const config = JSON.parse(fileContent)
49
49
 
50
50
  // Verify the file content
51
- expect(config).toHaveProperty('project.name', 'ChangeMePls')
51
+ expect(config).toHaveProperty('project.name', 'BaseProject')
52
52
  expect(config).toHaveProperty('paths.logs', 'logs/')
53
53
  expect(config).toHaveProperty('paths.tasks', 'src/tasks/')
54
54
  expect(config).toHaveProperty('infra.region', 'us-west-2')
@@ -77,7 +77,7 @@ describe('Init task', () => {
77
77
  expect(saveFileFn).not.toHaveBeenCalled()
78
78
 
79
79
  // Verify the returned config has the correct structure
80
- expect(result).toHaveProperty('project.name', 'ChangeMePls')
80
+ expect(result).toHaveProperty('project.name', 'BaseProject')
81
81
  expect(result).toHaveProperty('paths.logs', 'logs/')
82
82
  expect(result).toHaveProperty('paths.tasks', 'src/tasks/')
83
83
  expect(result).toHaveProperty('infra.region', 'us-west-2')