@forgehive/forge-cli 0.3.10 → 0.3.11
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 +25 -2
- package/dist/tasks/auth/list.d.ts +5 -1
- package/dist/tasks/auth/list.js +20 -8
- package/dist/tasks/auth/switch.js +24 -8
- package/dist/tasks/project/create.d.ts +27 -0
- package/dist/tasks/project/create.js +96 -0
- package/dist/tasks/project/link.d.ts +22 -0
- package/dist/tasks/project/link.js +95 -0
- package/dist/tasks/project/unlink.d.ts +11 -0
- package/dist/tasks/project/unlink.js +65 -0
- package/dist/tasks/task/createTask.js +5 -4
- package/dist/tasks/types.d.ts +1 -0
- package/dist/test/tasks/create.test.js +4 -3
- package/forge.json +14 -0
- package/package.json +4 -3
- package/src/runner.ts +25 -3
- package/src/tasks/auth/list.ts +22 -8
- package/src/tasks/auth/switch.ts +24 -8
- package/src/tasks/project/README.md +268 -0
- package/src/tasks/project/create.ts +111 -0
- package/src/tasks/project/link.ts +106 -0
- package/src/tasks/project/unlink.ts +74 -0
- package/src/tasks/task/createTask.ts +5 -4
- package/src/tasks/types.ts +1 -0
- package/src/test/tasks/create.test.ts +4 -3
package/dist/runner.js
CHANGED
|
@@ -29,6 +29,9 @@ const add_1 = require("./tasks/auth/add");
|
|
|
29
29
|
const switch_1 = require("./tasks/auth/switch");
|
|
30
30
|
const list_2 = require("./tasks/auth/list");
|
|
31
31
|
const remove_3 = require("./tasks/auth/remove");
|
|
32
|
+
const create_2 = require("./tasks/project/create");
|
|
33
|
+
const link_1 = require("./tasks/project/link");
|
|
34
|
+
const unlink_1 = require("./tasks/project/unlink");
|
|
32
35
|
const runner = new runner_1.Runner((data) => {
|
|
33
36
|
const { _, ...filteredObj } = data;
|
|
34
37
|
return {
|
|
@@ -64,6 +67,10 @@ runner.load('auth:add', add_1.add);
|
|
|
64
67
|
runner.load('auth:switch', switch_1.switchProfile);
|
|
65
68
|
runner.load('auth:list', list_2.list);
|
|
66
69
|
runner.load('auth:remove', remove_3.remove);
|
|
70
|
+
// Project commands
|
|
71
|
+
runner.load('project:create', create_2.create);
|
|
72
|
+
runner.load('project:link', link_1.link);
|
|
73
|
+
runner.load('project:unlink', unlink_1.unlink);
|
|
67
74
|
// Set handler
|
|
68
75
|
runner.setHandler(async (data) => {
|
|
69
76
|
const parsedArgs = runner.parseArguments(data);
|
|
@@ -146,7 +153,7 @@ runner.setHandler(async (data) => {
|
|
|
146
153
|
}
|
|
147
154
|
else if (taskName === 'auth:switch' || taskName === 'auth:remove') {
|
|
148
155
|
result = await task.run({
|
|
149
|
-
profileName: action
|
|
156
|
+
profileName: String(action)
|
|
150
157
|
});
|
|
151
158
|
}
|
|
152
159
|
else if (taskName === 'docs:download') {
|
|
@@ -155,13 +162,29 @@ runner.setHandler(async (data) => {
|
|
|
155
162
|
path
|
|
156
163
|
});
|
|
157
164
|
}
|
|
165
|
+
else if (taskName === 'project:create') {
|
|
166
|
+
const { projectName, description } = args;
|
|
167
|
+
result = await task.run({
|
|
168
|
+
projectName,
|
|
169
|
+
description
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
else if (taskName === 'project:link') {
|
|
173
|
+
const { uuid } = args;
|
|
174
|
+
result = await task.run({
|
|
175
|
+
uuid
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
else if (taskName === 'project:unlink') {
|
|
179
|
+
result = await task.run({});
|
|
180
|
+
}
|
|
158
181
|
else {
|
|
159
182
|
result = await task.run(args);
|
|
160
183
|
if (taskName === 'info') {
|
|
161
184
|
silent = true;
|
|
162
185
|
}
|
|
163
186
|
}
|
|
164
|
-
if (taskName === 'task:describe' || taskName === 'task:list') {
|
|
187
|
+
if (taskName === 'task:describe' || taskName === 'task:list' || taskName === 'auth:list') {
|
|
165
188
|
silent = true;
|
|
166
189
|
}
|
|
167
190
|
return {
|
|
@@ -7,8 +7,12 @@ export declare const list: import("@forgehive/task").TaskInstanceType<(argv: {},
|
|
|
7
7
|
default?: undefined;
|
|
8
8
|
} | {
|
|
9
9
|
default: string;
|
|
10
|
+
profiles: {
|
|
11
|
+
Name: string;
|
|
12
|
+
'API Key': string;
|
|
13
|
+
URL: string;
|
|
14
|
+
}[];
|
|
10
15
|
status?: undefined;
|
|
11
|
-
profiles?: undefined;
|
|
12
16
|
}>, {
|
|
13
17
|
loadProfiles: (args: {}) => Promise<Promise<Profiles>>;
|
|
14
18
|
}>;
|
package/dist/tasks/auth/list.js
CHANGED
|
@@ -20,16 +20,28 @@ exports.list = (0, task_1.createTask)({
|
|
|
20
20
|
console.log('No profiles found. Use auth:add to create one.');
|
|
21
21
|
return { status: 'Ok', profiles: [] };
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
profiles.profiles.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
console.log(
|
|
28
|
-
|
|
23
|
+
// Show current profile
|
|
24
|
+
const currentProfile = profiles.profiles.find(profile => profile.name === profiles.default);
|
|
25
|
+
if (currentProfile) {
|
|
26
|
+
console.log('Current Profile:');
|
|
27
|
+
console.log(` Name: ${currentProfile.name}`);
|
|
28
|
+
console.log(` API Key: ${currentProfile.apiKey}`);
|
|
29
|
+
console.log(` URL: ${currentProfile.url}`);
|
|
30
|
+
console.log('');
|
|
31
|
+
}
|
|
32
|
+
console.log('Available profiles:\n');
|
|
33
|
+
const tableData = profiles.profiles.map(profile => ({
|
|
34
|
+
Name: profile.name,
|
|
35
|
+
'API Key': profile.apiKey,
|
|
36
|
+
URL: profile.url
|
|
37
|
+
}));
|
|
38
|
+
console.table(tableData, ['Name', 'API Key', 'URL']);
|
|
29
39
|
console.log('\nUse auth:add to create or update a profile');
|
|
30
|
-
console.log('
|
|
40
|
+
console.log('Use auth:switch [name] or auth:switch [index] to switch profiles');
|
|
41
|
+
console.log('========================================');
|
|
31
42
|
return {
|
|
32
|
-
default: profiles.default
|
|
43
|
+
default: profiles.default,
|
|
44
|
+
profiles: tableData
|
|
33
45
|
};
|
|
34
46
|
}
|
|
35
47
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// TASK: switch
|
|
3
3
|
// Run this task with:
|
|
4
|
-
// forge
|
|
4
|
+
// forge auth:switch [name] or forge auth:switch [index]
|
|
5
5
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
6
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
7
|
};
|
|
@@ -30,18 +30,34 @@ exports.switchProfile = (0, task_1.createTask)({
|
|
|
30
30
|
fn: async function ({ profileName }, { loadProfiles, persistProfiles }) {
|
|
31
31
|
// Load profiles
|
|
32
32
|
const profiles = await loadProfiles({});
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
if (profiles.profiles.length === 0) {
|
|
34
|
+
throw new Error('No profiles found. Use auth:add to create one.');
|
|
35
|
+
}
|
|
36
|
+
let targetProfile;
|
|
37
|
+
// Check if profileName is a number (index)
|
|
38
|
+
const indexInput = parseInt(profileName, 10);
|
|
39
|
+
if (!isNaN(indexInput)) {
|
|
40
|
+
// Using index
|
|
41
|
+
if (indexInput < 0 || indexInput >= profiles.profiles.length) {
|
|
42
|
+
throw new Error(`Profile index ${indexInput} is out of range. Use auth:list to see available profiles (0-${profiles.profiles.length - 1}).`);
|
|
43
|
+
}
|
|
44
|
+
targetProfile = profiles.profiles[indexInput].name;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
// Using profile name
|
|
48
|
+
const profileExists = profiles.profiles.some(profile => profile.name === profileName);
|
|
49
|
+
if (!profileExists) {
|
|
50
|
+
throw new Error(`Profile "${profileName}" not found. Use auth:list to see available profiles.`);
|
|
51
|
+
}
|
|
52
|
+
targetProfile = profileName;
|
|
37
53
|
}
|
|
38
54
|
// Update default profile
|
|
39
|
-
profiles.default =
|
|
55
|
+
profiles.default = targetProfile;
|
|
40
56
|
// Save updated profiles
|
|
41
57
|
await persistProfiles(profiles);
|
|
42
|
-
console.log(`Switched to profile: ${
|
|
58
|
+
console.log(`Switched to profile: ${targetProfile}`);
|
|
43
59
|
return {
|
|
44
|
-
default:
|
|
60
|
+
default: targetProfile
|
|
45
61
|
};
|
|
46
62
|
}
|
|
47
63
|
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ForgeConf, type Profile } from '../types';
|
|
2
|
+
export declare const create: import("@forgehive/task").TaskInstanceType<(argv: {
|
|
3
|
+
description?: string | undefined;
|
|
4
|
+
projectName?: string | undefined;
|
|
5
|
+
}, boundaries: import("@forgehive/task").WrappedBoundaries<{
|
|
6
|
+
loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
|
|
7
|
+
loadCurrentProfile: (args: {}) => Promise<Promise<Profile>>;
|
|
8
|
+
writeFile: (filePath: string, content: string) => Promise<void>;
|
|
9
|
+
createProject: (profile: Profile, payload: {
|
|
10
|
+
projectName: string;
|
|
11
|
+
description: string;
|
|
12
|
+
uuid: string;
|
|
13
|
+
}) => Promise<Response>;
|
|
14
|
+
}>) => Promise<{
|
|
15
|
+
success: boolean;
|
|
16
|
+
project: any;
|
|
17
|
+
localUuid: string;
|
|
18
|
+
}>, {
|
|
19
|
+
loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
|
|
20
|
+
loadCurrentProfile: (args: {}) => Promise<Promise<Profile>>;
|
|
21
|
+
writeFile: (filePath: string, content: string) => Promise<void>;
|
|
22
|
+
createProject: (profile: Profile, payload: {
|
|
23
|
+
projectName: string;
|
|
24
|
+
description: string;
|
|
25
|
+
uuid: string;
|
|
26
|
+
}) => Promise<Response>;
|
|
27
|
+
}>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// TASK: create
|
|
3
|
+
// Run this task with:
|
|
4
|
+
// forge task:run project:create
|
|
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.create = void 0;
|
|
10
|
+
const task_1 = require("@forgehive/task");
|
|
11
|
+
const schema_1 = require("@forgehive/schema");
|
|
12
|
+
const uuid_1 = require("uuid");
|
|
13
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
14
|
+
const path_1 = __importDefault(require("path"));
|
|
15
|
+
const load_1 = require("../conf/load");
|
|
16
|
+
const loadCurrent_1 = require("../auth/loadCurrent");
|
|
17
|
+
const name = 'project:create';
|
|
18
|
+
const description = 'Create a new project in ForgeHive';
|
|
19
|
+
const schema = new schema_1.Schema({
|
|
20
|
+
projectName: schema_1.Schema.string().optional(),
|
|
21
|
+
description: schema_1.Schema.string().optional()
|
|
22
|
+
});
|
|
23
|
+
const boundaries = {
|
|
24
|
+
loadConf: load_1.load.asBoundary(),
|
|
25
|
+
loadCurrentProfile: loadCurrent_1.loadCurrent.asBoundary(),
|
|
26
|
+
writeFile: async (filePath, content) => {
|
|
27
|
+
await promises_1.default.writeFile(filePath, content, 'utf-8');
|
|
28
|
+
},
|
|
29
|
+
createProject: async (profile, payload) => {
|
|
30
|
+
const authToken = `${profile.apiKey}:${profile.apiSecret}`;
|
|
31
|
+
return await fetch(`${profile.url}/api/projects`, {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers: {
|
|
34
|
+
'Content-Type': 'application/json',
|
|
35
|
+
'Authorization': `Bearer ${authToken}`
|
|
36
|
+
},
|
|
37
|
+
body: JSON.stringify(payload)
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
exports.create = (0, task_1.createTask)({
|
|
42
|
+
name,
|
|
43
|
+
description,
|
|
44
|
+
schema,
|
|
45
|
+
boundaries,
|
|
46
|
+
fn: async function (argv, { loadConf, loadCurrentProfile, writeFile, createProject }) {
|
|
47
|
+
const { projectName: inputProjectName, description } = argv;
|
|
48
|
+
// Load current configuration
|
|
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
|
+
// 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));
|
|
69
|
+
console.log(`Generated and saved project UUID: ${projectUuid}`);
|
|
70
|
+
}
|
|
71
|
+
// Load current profile for API authentication
|
|
72
|
+
const profile = await loadCurrentProfile({});
|
|
73
|
+
// Prepare API request payload
|
|
74
|
+
const payload = {
|
|
75
|
+
projectName,
|
|
76
|
+
description: description || '',
|
|
77
|
+
uuid: projectUuid
|
|
78
|
+
};
|
|
79
|
+
// Make API request to create project
|
|
80
|
+
const response = await createProject(profile, payload);
|
|
81
|
+
if (!response.ok) {
|
|
82
|
+
const errorText = await response.text();
|
|
83
|
+
throw new Error(`Failed to create project: ${response.status} ${response.statusText} - ${errorText}`);
|
|
84
|
+
}
|
|
85
|
+
const result = await response.json();
|
|
86
|
+
console.log('Project created successfully!');
|
|
87
|
+
console.log(`Project UUID: ${result.project.uuid}`);
|
|
88
|
+
console.log(`Project Name: ${result.project.projectName}`);
|
|
89
|
+
console.log(`\n🌐 View your project on the dashboard: ${profile.url}/dashboard/projects/${result.project.uuid}`);
|
|
90
|
+
return {
|
|
91
|
+
success: true,
|
|
92
|
+
project: result.project,
|
|
93
|
+
localUuid: projectUuid
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ForgeConf, type Profile } from '../types';
|
|
2
|
+
export declare const link: import("@forgehive/task").TaskInstanceType<(argv: {
|
|
3
|
+
uuid: string;
|
|
4
|
+
}, boundaries: import("@forgehive/task").WrappedBoundaries<{
|
|
5
|
+
loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
|
|
6
|
+
loadCurrentProfile: (args: {}) => Promise<Promise<Profile>>;
|
|
7
|
+
writeFile: (filePath: string, content: string) => Promise<void>;
|
|
8
|
+
fetchProject: (profile: Profile, uuid: string) => Promise<Response>;
|
|
9
|
+
}>) => Promise<{
|
|
10
|
+
success: boolean;
|
|
11
|
+
linkedProject: {
|
|
12
|
+
uuid: any;
|
|
13
|
+
name: any;
|
|
14
|
+
description: any;
|
|
15
|
+
tasksCount: any;
|
|
16
|
+
};
|
|
17
|
+
}>, {
|
|
18
|
+
loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
|
|
19
|
+
loadCurrentProfile: (args: {}) => Promise<Promise<Profile>>;
|
|
20
|
+
writeFile: (filePath: string, content: string) => Promise<void>;
|
|
21
|
+
fetchProject: (profile: Profile, uuid: string) => Promise<Response>;
|
|
22
|
+
}>;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// TASK: link
|
|
3
|
+
// Run this task with:
|
|
4
|
+
// forge project:link [uuid]
|
|
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.link = 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 loadCurrent_1 = require("../auth/loadCurrent");
|
|
16
|
+
const name = 'project:link';
|
|
17
|
+
const description = 'Link an existing remote project to the local project by UUID';
|
|
18
|
+
const schema = new schema_1.Schema({
|
|
19
|
+
uuid: schema_1.Schema.string()
|
|
20
|
+
});
|
|
21
|
+
const boundaries = {
|
|
22
|
+
loadConf: load_1.load.asBoundary(),
|
|
23
|
+
loadCurrentProfile: loadCurrent_1.loadCurrent.asBoundary(),
|
|
24
|
+
writeFile: async (filePath, content) => {
|
|
25
|
+
await promises_1.default.writeFile(filePath, content, 'utf-8');
|
|
26
|
+
},
|
|
27
|
+
fetchProject: async (profile, uuid) => {
|
|
28
|
+
const authToken = `${profile.apiKey}:${profile.apiSecret}`;
|
|
29
|
+
console.log(`Fetching project ${uuid} from ${profile.url}...`);
|
|
30
|
+
return await fetch(`${profile.url}/api/projects/${uuid}`, {
|
|
31
|
+
method: 'GET',
|
|
32
|
+
headers: {
|
|
33
|
+
'Authorization': `Bearer ${authToken}`
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
exports.link = (0, task_1.createTask)({
|
|
39
|
+
name,
|
|
40
|
+
description,
|
|
41
|
+
schema,
|
|
42
|
+
boundaries,
|
|
43
|
+
fn: async function ({ uuid }, { loadConf, loadCurrentProfile, writeFile, fetchProject }) {
|
|
44
|
+
// Load current configuration and profile
|
|
45
|
+
const conf = await loadConf({});
|
|
46
|
+
// Check if project already has a UUID
|
|
47
|
+
if (conf.project.uuid) {
|
|
48
|
+
throw new Error(`Project is already linked to UUID: ${conf.project.uuid}. Use a different project or remove the existing UUID from forge.json first.`);
|
|
49
|
+
}
|
|
50
|
+
const profile = await loadCurrentProfile({});
|
|
51
|
+
console.log(`Checking if project ${uuid} exists on ${profile.url}...`);
|
|
52
|
+
// Check if project exists on remote
|
|
53
|
+
const response = await fetchProject(profile, uuid);
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
if (response.status === 404) {
|
|
56
|
+
throw new Error(`Project with UUID ${uuid} not found on ${profile.url}. Please verify the UUID is correct.`);
|
|
57
|
+
}
|
|
58
|
+
else if (response.status === 401) {
|
|
59
|
+
throw new Error('Authentication failed. Please check your profile credentials with \'forge auth:list\'.');
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
const errorText = await response.text();
|
|
63
|
+
throw new Error(`Failed to verify project: ${response.status} ${response.statusText} - ${errorText}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const projectData = await response.json();
|
|
67
|
+
const project = projectData.project;
|
|
68
|
+
console.log(`✓ Found project: ${project.projectName}`);
|
|
69
|
+
console.log(` Description: ${project.description || 'No description'}`);
|
|
70
|
+
console.log(` Tasks: ${project.tasks.length} task(s)`);
|
|
71
|
+
// Update forge.json with the UUID
|
|
72
|
+
const forgePath = path_1.default.join(process.cwd(), 'forge.json');
|
|
73
|
+
const updatedConf = {
|
|
74
|
+
...conf,
|
|
75
|
+
project: {
|
|
76
|
+
...conf.project,
|
|
77
|
+
uuid: uuid
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
await writeFile(forgePath, JSON.stringify(updatedConf, null, 2));
|
|
81
|
+
console.log(`\n✓ Successfully linked project ${uuid} to local forge.json`);
|
|
82
|
+
console.log(` Local project name: ${conf.project.name}`);
|
|
83
|
+
console.log(` Remote project name: ${project.projectName}`);
|
|
84
|
+
console.log(`\n🌐 View your project on the dashboard: ${profile.url}/dashboard/projects/${uuid}`);
|
|
85
|
+
return {
|
|
86
|
+
success: true,
|
|
87
|
+
linkedProject: {
|
|
88
|
+
uuid: project.uuid,
|
|
89
|
+
name: project.projectName,
|
|
90
|
+
description: project.description,
|
|
91
|
+
tasksCount: project.tasks.length
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
});
|
|
@@ -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
|
+
});
|
|
@@ -6,6 +6,7 @@ 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"));
|
|
@@ -40,11 +41,10 @@ export const {{ taskName }} = createTask({
|
|
|
40
41
|
boundaries,
|
|
41
42
|
fn: async function (argv, boundaries) {
|
|
42
43
|
console.log('input:', argv)
|
|
43
|
-
console.log('boundaries:', boundaries)
|
|
44
|
+
console.log('boundaries:', Object.keys(boundaries))
|
|
44
45
|
// Your task implementation goes here
|
|
45
|
-
const status = { status: 'Ok' }
|
|
46
46
|
|
|
47
|
-
return
|
|
47
|
+
return {}
|
|
48
48
|
}
|
|
49
49
|
})
|
|
50
50
|
|
|
@@ -134,7 +134,8 @@ exports.createTaskCommand = (0, task_1.createTask)({
|
|
|
134
134
|
}
|
|
135
135
|
forge.tasks[descriptor] = {
|
|
136
136
|
path: `${taskPath}/${fileName}`,
|
|
137
|
-
handler: taskName
|
|
137
|
+
handler: taskName,
|
|
138
|
+
uuid: (0, uuid_1.v4)()
|
|
138
139
|
};
|
|
139
140
|
await persistConf(forge, cwd);
|
|
140
141
|
return { taskPath, fileName };
|
package/dist/tasks/types.d.ts
CHANGED
|
@@ -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
|
|
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.
|
|
3
|
+
"version": "0.3.11",
|
|
4
4
|
"description": "TypeScript CLI application",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -29,11 +29,12 @@
|
|
|
29
29
|
"handlebars": "^4.7.8",
|
|
30
30
|
"minimist": "^1.2.8",
|
|
31
31
|
"typescript": "^5.3.3",
|
|
32
|
+
"uuid": "^11.1.0",
|
|
32
33
|
"@forgehive/hive-sdk": "0.1.2",
|
|
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",
|
package/src/runner.ts
CHANGED
|
@@ -20,7 +20,6 @@ import { describe as describeTask } from './tasks/task/describe'
|
|
|
20
20
|
import { fingerprint as fingerprintTask } from './tasks/task/fingerprint'
|
|
21
21
|
import { invoke as invokeTask } from './tasks/task/invoke'
|
|
22
22
|
|
|
23
|
-
|
|
24
23
|
import { create as createRunner } from './tasks/runner/create'
|
|
25
24
|
import { remove as removeRunner } from './tasks/runner/remove'
|
|
26
25
|
import { bundle as bundleRunner } from './tasks/runner/bundle'
|
|
@@ -34,6 +33,10 @@ import { switchProfile } from './tasks/auth/switch'
|
|
|
34
33
|
import { list as listProfiles } from './tasks/auth/list'
|
|
35
34
|
import { remove as removeProfile } from './tasks/auth/remove'
|
|
36
35
|
|
|
36
|
+
import { create as createProject } from './tasks/project/create'
|
|
37
|
+
import { link as linkProject } from './tasks/project/link'
|
|
38
|
+
import { unlink as unlinkProject } from './tasks/project/unlink'
|
|
39
|
+
|
|
37
40
|
interface CliParsedArguments extends RunnerParsedArguments {
|
|
38
41
|
action: string;
|
|
39
42
|
}
|
|
@@ -81,6 +84,11 @@ runner.load('auth:switch', switchProfile)
|
|
|
81
84
|
runner.load('auth:list', listProfiles)
|
|
82
85
|
runner.load('auth:remove', removeProfile)
|
|
83
86
|
|
|
87
|
+
// Project commands
|
|
88
|
+
runner.load('project:create', createProject)
|
|
89
|
+
runner.load('project:link', linkProject)
|
|
90
|
+
runner.load('project:unlink', unlinkProject)
|
|
91
|
+
|
|
84
92
|
// Set handler
|
|
85
93
|
runner.setHandler(async (data: ParsedArgs): Promise<unknown> => {
|
|
86
94
|
const parsedArgs = runner.parseArguments(data)
|
|
@@ -167,7 +175,7 @@ runner.setHandler(async (data: ParsedArgs): Promise<unknown> => {
|
|
|
167
175
|
})
|
|
168
176
|
} else if (taskName === 'auth:switch' || taskName === 'auth:remove') {
|
|
169
177
|
result = await task.run({
|
|
170
|
-
profileName: action
|
|
178
|
+
profileName: String(action)
|
|
171
179
|
})
|
|
172
180
|
} else if (taskName === 'docs:download') {
|
|
173
181
|
const { path } = args as { path?: string }
|
|
@@ -175,6 +183,20 @@ runner.setHandler(async (data: ParsedArgs): Promise<unknown> => {
|
|
|
175
183
|
result = await task.run({
|
|
176
184
|
path
|
|
177
185
|
})
|
|
186
|
+
} else if (taskName === 'project:create') {
|
|
187
|
+
const { projectName, description } = args as { projectName?: string, description?: string }
|
|
188
|
+
|
|
189
|
+
result = await task.run({
|
|
190
|
+
projectName,
|
|
191
|
+
description
|
|
192
|
+
})
|
|
193
|
+
} else if (taskName === 'project:link') {
|
|
194
|
+
const { uuid } = args as { uuid: string }
|
|
195
|
+
result = await task.run({
|
|
196
|
+
uuid
|
|
197
|
+
})
|
|
198
|
+
} else if (taskName === 'project:unlink') {
|
|
199
|
+
result = await task.run({})
|
|
178
200
|
} else {
|
|
179
201
|
result = await task.run(args)
|
|
180
202
|
|
|
@@ -183,7 +205,7 @@ runner.setHandler(async (data: ParsedArgs): Promise<unknown> => {
|
|
|
183
205
|
}
|
|
184
206
|
}
|
|
185
207
|
|
|
186
|
-
if (taskName === 'task:describe' || taskName === 'task:list') {
|
|
208
|
+
if (taskName === 'task:describe' || taskName === 'task:list' || taskName === 'auth:list') {
|
|
187
209
|
silent = true
|
|
188
210
|
}
|
|
189
211
|
|