@relesio/cli 0.2.6
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/README.md +224 -0
- package/bin/relesio.js +4 -0
- package/dist/commands/auth/index.d.ts +2 -0
- package/dist/commands/auth/index.js +9 -0
- package/dist/commands/auth/login.d.ts +2 -0
- package/dist/commands/auth/login.js +82 -0
- package/dist/commands/auth/logout.d.ts +2 -0
- package/dist/commands/auth/logout.js +25 -0
- package/dist/commands/auth/status.d.ts +2 -0
- package/dist/commands/auth/status.js +35 -0
- package/dist/commands/deploy.d.ts +2 -0
- package/dist/commands/deploy.js +293 -0
- package/dist/commands/organizations/index.d.ts +2 -0
- package/dist/commands/organizations/index.js +8 -0
- package/dist/commands/organizations/list.d.ts +2 -0
- package/dist/commands/organizations/list.js +54 -0
- package/dist/commands/organizations/set.d.ts +2 -0
- package/dist/commands/organizations/set.js +34 -0
- package/dist/commands/projects/index.d.ts +2 -0
- package/dist/commands/projects/index.js +6 -0
- package/dist/commands/projects/list.d.ts +2 -0
- package/dist/commands/projects/list.js +49 -0
- package/dist/commands/rollback.d.ts +2 -0
- package/dist/commands/rollback.js +119 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +59 -0
- package/dist/commands/team/get.d.ts +2 -0
- package/dist/commands/team/get.js +34 -0
- package/dist/commands/team/index.d.ts +2 -0
- package/dist/commands/team/index.js +7 -0
- package/dist/commands/team/list.d.ts +2 -0
- package/dist/commands/team/list.js +46 -0
- package/dist/commands/upload.d.ts +2 -0
- package/dist/commands/upload.js +365 -0
- package/dist/commands/versions/index.d.ts +2 -0
- package/dist/commands/versions/index.js +6 -0
- package/dist/commands/versions/list.d.ts +2 -0
- package/dist/commands/versions/list.js +51 -0
- package/dist/commands/whoami.d.ts +2 -0
- package/dist/commands/whoami.js +52 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +90 -0
- package/dist/lib/api/client.d.ts +32 -0
- package/dist/lib/api/client.js +187 -0
- package/dist/lib/api/deployments.d.ts +122 -0
- package/dist/lib/api/deployments.js +55 -0
- package/dist/lib/api/organizations.d.ts +27 -0
- package/dist/lib/api/organizations.js +37 -0
- package/dist/lib/api/projects.d.ts +38 -0
- package/dist/lib/api/projects.js +34 -0
- package/dist/lib/api/teams.d.ts +15 -0
- package/dist/lib/api/teams.js +44 -0
- package/dist/lib/api/types.d.ts +89 -0
- package/dist/lib/api/types.js +2 -0
- package/dist/lib/api/versions.d.ts +140 -0
- package/dist/lib/api/versions.js +53 -0
- package/dist/lib/config/manager.d.ts +35 -0
- package/dist/lib/config/manager.js +78 -0
- package/dist/lib/config/types.d.ts +15 -0
- package/dist/lib/config/types.js +15 -0
- package/dist/lib/errors/handler.d.ts +1 -0
- package/dist/lib/errors/handler.js +75 -0
- package/dist/lib/errors/types.d.ts +19 -0
- package/dist/lib/errors/types.js +34 -0
- package/dist/lib/files/scanner.d.ts +23 -0
- package/dist/lib/files/scanner.js +81 -0
- package/dist/lib/git/metadata.d.ts +31 -0
- package/dist/lib/git/metadata.js +90 -0
- package/dist/lib/output/colors.d.ts +14 -0
- package/dist/lib/output/colors.js +23 -0
- package/dist/lib/output/formatter.d.ts +2 -0
- package/dist/lib/output/formatter.js +7 -0
- package/dist/lib/output/table.d.ts +7 -0
- package/dist/lib/output/table.js +64 -0
- package/package.json +58 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export interface RequestOptions {
|
|
2
|
+
headers?: Record<string, string>;
|
|
3
|
+
query?: Record<string, string | number | boolean | undefined>;
|
|
4
|
+
timeout?: number;
|
|
5
|
+
/**
|
|
6
|
+
* When true, return the full API envelope (including meta/message)
|
|
7
|
+
* instead of unwrapping the `data` field.
|
|
8
|
+
*/
|
|
9
|
+
raw?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Organization ID to set in x-organization-id header
|
|
12
|
+
* Overrides default org from config
|
|
13
|
+
*/
|
|
14
|
+
orgId?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface APIResponse<T = unknown> {
|
|
17
|
+
message: string;
|
|
18
|
+
data: T;
|
|
19
|
+
pagination?: {
|
|
20
|
+
limit: number;
|
|
21
|
+
offset: number;
|
|
22
|
+
total: number;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export interface Organization {
|
|
26
|
+
id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
slug: string | null;
|
|
29
|
+
imageUrl: string;
|
|
30
|
+
createdAt: string;
|
|
31
|
+
membersCount?: number;
|
|
32
|
+
}
|
|
33
|
+
export interface CreateOrganizationData {
|
|
34
|
+
name: string;
|
|
35
|
+
slug?: string;
|
|
36
|
+
publicMetadata?: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
export interface UpdateOrganizationData {
|
|
39
|
+
name?: string;
|
|
40
|
+
slug?: string;
|
|
41
|
+
publicMetadata?: Record<string, unknown>;
|
|
42
|
+
}
|
|
43
|
+
export interface UserOrganization {
|
|
44
|
+
id: string;
|
|
45
|
+
name: string;
|
|
46
|
+
slug: string | null;
|
|
47
|
+
imageUrl: string;
|
|
48
|
+
isCurrent: boolean;
|
|
49
|
+
role: string;
|
|
50
|
+
}
|
|
51
|
+
export interface UserOrganizationsResponse {
|
|
52
|
+
organizations: UserOrganization[];
|
|
53
|
+
currentOrganizationId: string | null;
|
|
54
|
+
}
|
|
55
|
+
export interface Team {
|
|
56
|
+
id: string;
|
|
57
|
+
organizationId: string;
|
|
58
|
+
name: string;
|
|
59
|
+
description: string | null;
|
|
60
|
+
createdAt: string;
|
|
61
|
+
updatedAt: string;
|
|
62
|
+
memberCount?: number;
|
|
63
|
+
}
|
|
64
|
+
export interface CreateTeamData {
|
|
65
|
+
name: string;
|
|
66
|
+
description?: string;
|
|
67
|
+
}
|
|
68
|
+
export interface UpdateTeamData {
|
|
69
|
+
name?: string;
|
|
70
|
+
description?: string;
|
|
71
|
+
}
|
|
72
|
+
export interface TeamMember {
|
|
73
|
+
id: string;
|
|
74
|
+
teamId: string;
|
|
75
|
+
userId: string;
|
|
76
|
+
role: string;
|
|
77
|
+
createdAt: string;
|
|
78
|
+
}
|
|
79
|
+
export interface AddTeamMemberData {
|
|
80
|
+
userId: string;
|
|
81
|
+
role?: "admin" | "member";
|
|
82
|
+
}
|
|
83
|
+
export interface UpdateTeamMemberData {
|
|
84
|
+
role: "admin" | "member";
|
|
85
|
+
}
|
|
86
|
+
export interface PaginationQuery extends Record<string, number | undefined> {
|
|
87
|
+
limit?: number;
|
|
88
|
+
offset?: number;
|
|
89
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import type { RelesioAPIClient } from "./client.js";
|
|
2
|
+
import type { GitMetadata } from "../git/metadata.js";
|
|
3
|
+
export interface Version {
|
|
4
|
+
id: string;
|
|
5
|
+
projectId: string;
|
|
6
|
+
version?: string;
|
|
7
|
+
snapshotId?: string;
|
|
8
|
+
semanticVersion?: string | null;
|
|
9
|
+
username?: string;
|
|
10
|
+
uploadId?: number;
|
|
11
|
+
gitSha?: string;
|
|
12
|
+
gitBranch?: string;
|
|
13
|
+
gitCommitMessage?: string;
|
|
14
|
+
gitAuthor?: string;
|
|
15
|
+
gitTag?: string | null;
|
|
16
|
+
uploadedBy: string;
|
|
17
|
+
uploadedByType?: string;
|
|
18
|
+
uploadedByName?: string;
|
|
19
|
+
uploadDate: string;
|
|
20
|
+
fileCount: number;
|
|
21
|
+
totalSizeBytes: number;
|
|
22
|
+
r2Prefix?: string;
|
|
23
|
+
status: "pending" | "uploading" | "completed" | "failed";
|
|
24
|
+
dependencies?: unknown[] | string;
|
|
25
|
+
metadata?: Record<string, unknown> | string;
|
|
26
|
+
createdAt: string;
|
|
27
|
+
updatedAt: string;
|
|
28
|
+
}
|
|
29
|
+
export interface FileManifest {
|
|
30
|
+
path: string;
|
|
31
|
+
contentType: string;
|
|
32
|
+
size: number;
|
|
33
|
+
checksum: string;
|
|
34
|
+
}
|
|
35
|
+
export interface CreateVersionInput {
|
|
36
|
+
version: string;
|
|
37
|
+
fileCount: number;
|
|
38
|
+
totalSizeBytes: number;
|
|
39
|
+
files: FileManifest[];
|
|
40
|
+
packageName?: string;
|
|
41
|
+
semanticVersion?: string;
|
|
42
|
+
dependencies?: unknown[];
|
|
43
|
+
metadata?: Record<string, unknown>;
|
|
44
|
+
gitSha?: string;
|
|
45
|
+
gitBranch?: string;
|
|
46
|
+
gitCommitMessage?: string;
|
|
47
|
+
gitAuthor?: string;
|
|
48
|
+
gitTag?: string;
|
|
49
|
+
}
|
|
50
|
+
export interface UploadUrl {
|
|
51
|
+
filePath: string;
|
|
52
|
+
r2Key: string;
|
|
53
|
+
uploadUrl: string;
|
|
54
|
+
}
|
|
55
|
+
export interface CreateVersionResponse {
|
|
56
|
+
versionId: string;
|
|
57
|
+
uploadUrls: UploadUrl[];
|
|
58
|
+
copiedFiles?: string[];
|
|
59
|
+
failedCopies?: Array<{
|
|
60
|
+
filePath: string;
|
|
61
|
+
reason: string;
|
|
62
|
+
}>;
|
|
63
|
+
stats?: {
|
|
64
|
+
toUpload: number;
|
|
65
|
+
copied: number;
|
|
66
|
+
failed: number;
|
|
67
|
+
uploadSizeBytes: number;
|
|
68
|
+
savedBytes: number;
|
|
69
|
+
alreadyExists?: boolean;
|
|
70
|
+
existingFileCount?: number;
|
|
71
|
+
};
|
|
72
|
+
previewUrl?: string;
|
|
73
|
+
message: string;
|
|
74
|
+
}
|
|
75
|
+
export interface Asset {
|
|
76
|
+
id: string;
|
|
77
|
+
versionId: string;
|
|
78
|
+
filePath: string;
|
|
79
|
+
sizeBytes: number;
|
|
80
|
+
contentType: string;
|
|
81
|
+
r2Key: string;
|
|
82
|
+
checksum: string;
|
|
83
|
+
createdAt: string;
|
|
84
|
+
}
|
|
85
|
+
export interface CompleteVersionInput {
|
|
86
|
+
assets: Array<{
|
|
87
|
+
filePath: string;
|
|
88
|
+
sizeBytes: number;
|
|
89
|
+
contentType: string;
|
|
90
|
+
r2Key: string;
|
|
91
|
+
checksum: string;
|
|
92
|
+
}>;
|
|
93
|
+
copiedAssets?: string[];
|
|
94
|
+
}
|
|
95
|
+
export interface CompleteVersionResponse {
|
|
96
|
+
versionId: string;
|
|
97
|
+
previewUrl: string;
|
|
98
|
+
fileCount: number;
|
|
99
|
+
totalSize: number;
|
|
100
|
+
message: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Create a new version (initiate upload)
|
|
104
|
+
*/
|
|
105
|
+
export declare function createVersion(client: RelesioAPIClient, projectId: string, input: CreateVersionInput): Promise<CreateVersionResponse>;
|
|
106
|
+
/**
|
|
107
|
+
* Complete version upload
|
|
108
|
+
*/
|
|
109
|
+
export declare function completeVersion(client: RelesioAPIClient, projectId: string, versionId: string, input: CompleteVersionInput): Promise<CompleteVersionResponse>;
|
|
110
|
+
/**
|
|
111
|
+
* List versions for a project
|
|
112
|
+
*/
|
|
113
|
+
export declare function listVersions(client: RelesioAPIClient, projectId: string): Promise<{
|
|
114
|
+
versions: Version[];
|
|
115
|
+
count: number;
|
|
116
|
+
}>;
|
|
117
|
+
/**
|
|
118
|
+
* Get version details
|
|
119
|
+
*/
|
|
120
|
+
export declare function getVersion(client: RelesioAPIClient, projectId: string, versionId: string): Promise<{
|
|
121
|
+
version: Version;
|
|
122
|
+
}>;
|
|
123
|
+
/**
|
|
124
|
+
* List assets for a version
|
|
125
|
+
*/
|
|
126
|
+
export declare function listAssets(client: RelesioAPIClient, projectId: string, versionId: string): Promise<{
|
|
127
|
+
assets: Asset[];
|
|
128
|
+
count: number;
|
|
129
|
+
totalSize: number;
|
|
130
|
+
}>;
|
|
131
|
+
/**
|
|
132
|
+
* Helper: Convert git metadata to API format
|
|
133
|
+
*/
|
|
134
|
+
export declare function gitMetadataToApiFormat(metadata: GitMetadata): {
|
|
135
|
+
gitSha?: string;
|
|
136
|
+
gitBranch?: string;
|
|
137
|
+
gitCommitMessage?: string;
|
|
138
|
+
gitAuthor?: string;
|
|
139
|
+
gitTag?: string;
|
|
140
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a new version (initiate upload)
|
|
3
|
+
*/
|
|
4
|
+
export async function createVersion(client, projectId, input) {
|
|
5
|
+
return client.post(`/v1/projects/${projectId}/versions`, input);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Complete version upload
|
|
9
|
+
*/
|
|
10
|
+
export async function completeVersion(client, projectId, versionId, input) {
|
|
11
|
+
return client.post(`/v1/projects/${projectId}/versions/${versionId}/complete`, input);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* List versions for a project
|
|
15
|
+
*/
|
|
16
|
+
export async function listVersions(client, projectId) {
|
|
17
|
+
const response = await client.get(`/v1/projects/${projectId}/versions`, { raw: true });
|
|
18
|
+
return {
|
|
19
|
+
versions: response.data,
|
|
20
|
+
count: response.meta?.count ?? response.data.length
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Get version details
|
|
25
|
+
*/
|
|
26
|
+
export async function getVersion(client, projectId, versionId) {
|
|
27
|
+
const response = await client.get(`/v1/projects/${projectId}/versions/${versionId}`, { raw: true });
|
|
28
|
+
return { version: response.data };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* List assets for a version
|
|
32
|
+
*/
|
|
33
|
+
export async function listAssets(client, projectId, versionId) {
|
|
34
|
+
const response = await client.get(`/v1/projects/${projectId}/versions/${versionId}/assets`, { raw: true });
|
|
35
|
+
return {
|
|
36
|
+
assets: response.data,
|
|
37
|
+
count: response.meta?.count ?? response.data.length,
|
|
38
|
+
totalSize: response.meta?.totalSize ??
|
|
39
|
+
response.data.reduce((sum, a) => sum + (a.sizeBytes ?? 0), 0)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Helper: Convert git metadata to API format
|
|
44
|
+
*/
|
|
45
|
+
export function gitMetadataToApiFormat(metadata) {
|
|
46
|
+
return {
|
|
47
|
+
gitSha: metadata.sha,
|
|
48
|
+
gitBranch: metadata.branch,
|
|
49
|
+
gitCommitMessage: metadata.commitMessage,
|
|
50
|
+
gitAuthor: metadata.author,
|
|
51
|
+
gitTag: metadata.tag
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { RelesioConfig } from "./types.js";
|
|
2
|
+
declare class ConfigManagerClass {
|
|
3
|
+
private conf;
|
|
4
|
+
constructor();
|
|
5
|
+
/**
|
|
6
|
+
* Load the full configuration, merging environment variables with config file
|
|
7
|
+
*/
|
|
8
|
+
load(): RelesioConfig;
|
|
9
|
+
/**
|
|
10
|
+
* Save partial configuration (merges with existing)
|
|
11
|
+
*/
|
|
12
|
+
save(partialConfig: Partial<RelesioConfig>): void;
|
|
13
|
+
/**
|
|
14
|
+
* Get a specific config value
|
|
15
|
+
*/
|
|
16
|
+
get<K extends keyof RelesioConfig>(key: K): RelesioConfig[K];
|
|
17
|
+
/**
|
|
18
|
+
* Set a specific config value
|
|
19
|
+
*/
|
|
20
|
+
set<K extends keyof RelesioConfig>(key: K, value: RelesioConfig[K]): void;
|
|
21
|
+
/**
|
|
22
|
+
* Clear all configuration (logout)
|
|
23
|
+
*/
|
|
24
|
+
clear(): void;
|
|
25
|
+
/**
|
|
26
|
+
* Get the config file path
|
|
27
|
+
*/
|
|
28
|
+
getConfigPath(): string;
|
|
29
|
+
/**
|
|
30
|
+
* Check if user is authenticated
|
|
31
|
+
*/
|
|
32
|
+
isAuthenticated(): boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare const ConfigManager: ConfigManagerClass;
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import Conf from "conf";
|
|
2
|
+
import { DEFAULT_CONFIG } from "./types.js";
|
|
3
|
+
class ConfigManagerClass {
|
|
4
|
+
conf;
|
|
5
|
+
constructor() {
|
|
6
|
+
this.conf = new Conf({
|
|
7
|
+
projectName: "relesio",
|
|
8
|
+
defaults: DEFAULT_CONFIG
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Load the full configuration, merging environment variables with config file
|
|
13
|
+
*/
|
|
14
|
+
load() {
|
|
15
|
+
const config = this.conf.store;
|
|
16
|
+
// Override with environment variables if present
|
|
17
|
+
return {
|
|
18
|
+
...config,
|
|
19
|
+
apiToken: process.env.RELESIO_API_TOKEN || config.apiToken,
|
|
20
|
+
apiBaseUrl: process.env.RELESIO_API_URL || config.apiBaseUrl,
|
|
21
|
+
activeOrganizationId: process.env.RELESIO_ORG_ID || config.activeOrganizationId,
|
|
22
|
+
// Legacy field for backward compatibility
|
|
23
|
+
currentOrgId: process.env.RELESIO_ORG_ID || config.currentOrgId
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Save partial configuration (merges with existing)
|
|
28
|
+
*/
|
|
29
|
+
save(partialConfig) {
|
|
30
|
+
for (const [key, value] of Object.entries(partialConfig)) {
|
|
31
|
+
this.conf.set(key, value);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get a specific config value
|
|
36
|
+
*/
|
|
37
|
+
get(key) {
|
|
38
|
+
// Check environment variable first
|
|
39
|
+
if (key === "apiToken" && process.env.RELESIO_API_TOKEN) {
|
|
40
|
+
return process.env.RELESIO_API_TOKEN;
|
|
41
|
+
}
|
|
42
|
+
if (key === "apiBaseUrl" && process.env.RELESIO_API_URL) {
|
|
43
|
+
return process.env.RELESIO_API_URL;
|
|
44
|
+
}
|
|
45
|
+
if ((key === "activeOrganizationId" || key === "currentOrgId") &&
|
|
46
|
+
process.env.RELESIO_ORG_ID) {
|
|
47
|
+
return process.env.RELESIO_ORG_ID;
|
|
48
|
+
}
|
|
49
|
+
return this.conf.get(key);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Set a specific config value
|
|
53
|
+
*/
|
|
54
|
+
set(key, value) {
|
|
55
|
+
this.conf.set(key, value);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Clear all configuration (logout)
|
|
59
|
+
*/
|
|
60
|
+
clear() {
|
|
61
|
+
this.conf.clear();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get the config file path
|
|
65
|
+
*/
|
|
66
|
+
getConfigPath() {
|
|
67
|
+
return this.conf.path;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Check if user is authenticated
|
|
71
|
+
*/
|
|
72
|
+
isAuthenticated() {
|
|
73
|
+
const token = this.get("apiToken");
|
|
74
|
+
return token !== null && token !== "";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Export singleton instance
|
|
78
|
+
export const ConfigManager = new ConfigManagerClass();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface RelesioConfig {
|
|
2
|
+
apiToken: string | null;
|
|
3
|
+
apiBaseUrl: string;
|
|
4
|
+
userId: string | null;
|
|
5
|
+
userEmail: string | null;
|
|
6
|
+
userName: string | null;
|
|
7
|
+
activeOrganizationId: string | null;
|
|
8
|
+
activeOrganizationName: string | null;
|
|
9
|
+
activeOrganizationSlug: string | null;
|
|
10
|
+
currentOrgId: string | null;
|
|
11
|
+
currentOrgName: string | null;
|
|
12
|
+
output: "json" | "table";
|
|
13
|
+
colorOutput: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare const DEFAULT_CONFIG: RelesioConfig;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const DEFAULT_CONFIG = {
|
|
2
|
+
apiToken: null,
|
|
3
|
+
apiBaseUrl: "https://api.relesio.com",
|
|
4
|
+
userId: null,
|
|
5
|
+
userEmail: null,
|
|
6
|
+
userName: null,
|
|
7
|
+
activeOrganizationId: null,
|
|
8
|
+
activeOrganizationName: null,
|
|
9
|
+
activeOrganizationSlug: null,
|
|
10
|
+
// Legacy fields
|
|
11
|
+
currentOrgId: null,
|
|
12
|
+
currentOrgName: null,
|
|
13
|
+
output: "table",
|
|
14
|
+
colorOutput: true
|
|
15
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function handleError(error: unknown): never;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { AuthenticationError, APIError, ConfigError, ValidationError } from "./types.js";
|
|
3
|
+
import { APIError as ClientAPIError } from "../api/client.js";
|
|
4
|
+
export function handleError(error) {
|
|
5
|
+
// Convert non-Error values to Error
|
|
6
|
+
if (!(error instanceof Error)) {
|
|
7
|
+
const stringError = typeof error === "string" ? error : JSON.stringify(error);
|
|
8
|
+
error = new Error(stringError);
|
|
9
|
+
}
|
|
10
|
+
// Authentication errors
|
|
11
|
+
if (error instanceof AuthenticationError) {
|
|
12
|
+
console.error(chalk.red("\n✗ Authentication Error"));
|
|
13
|
+
console.error(chalk.dim(error.message));
|
|
14
|
+
console.error(chalk.yellow("\nTip: Run 'relesio auth login' to authenticate"));
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
// API errors (from client)
|
|
18
|
+
if (error instanceof ClientAPIError || error instanceof APIError) {
|
|
19
|
+
const apiError = error;
|
|
20
|
+
console.error(chalk.red(`\n✗ API Error (${apiError.statusCode})`));
|
|
21
|
+
console.error(chalk.dim(apiError.message));
|
|
22
|
+
// Show response details in debug mode
|
|
23
|
+
if (process.env.DEBUG) {
|
|
24
|
+
console.error(chalk.dim("\n[DEBUG] Full Error Details:"));
|
|
25
|
+
console.error(chalk.dim(JSON.stringify({
|
|
26
|
+
message: apiError.message,
|
|
27
|
+
statusCode: apiError.statusCode,
|
|
28
|
+
response: apiError.response,
|
|
29
|
+
stack: apiError.stack
|
|
30
|
+
}, null, 2)));
|
|
31
|
+
}
|
|
32
|
+
// Specific status code handling
|
|
33
|
+
if (apiError.statusCode === 401) {
|
|
34
|
+
console.error(chalk.yellow("\nYour authentication may have expired. Try running 'relesio auth login'"));
|
|
35
|
+
}
|
|
36
|
+
else if (apiError.statusCode === 403) {
|
|
37
|
+
console.error(chalk.yellow("\nYou don't have permission to perform this action"));
|
|
38
|
+
}
|
|
39
|
+
else if (apiError.statusCode === 404) {
|
|
40
|
+
console.error(chalk.yellow("\nThe requested resource was not found"));
|
|
41
|
+
}
|
|
42
|
+
else if (apiError.statusCode === 0) {
|
|
43
|
+
console.error(chalk.yellow("\nNetwork error. Check your internet connection and try again"));
|
|
44
|
+
}
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
// Config errors
|
|
48
|
+
if (error instanceof ConfigError) {
|
|
49
|
+
console.error(chalk.red("\n✗ Configuration Error"));
|
|
50
|
+
console.error(chalk.dim(error.message));
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
// Validation errors
|
|
54
|
+
if (error instanceof ValidationError) {
|
|
55
|
+
console.error(chalk.red("\n✗ Validation Error"));
|
|
56
|
+
console.error(chalk.dim(error.message));
|
|
57
|
+
if (error.details) {
|
|
58
|
+
console.error(chalk.dim("\nDetails:"));
|
|
59
|
+
console.error(chalk.dim(JSON.stringify(error.details, null, 2)));
|
|
60
|
+
}
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
// Unknown errors (at this point, error is guaranteed to be an Error instance)
|
|
64
|
+
const err = error;
|
|
65
|
+
console.error(chalk.red("\n✗ Unexpected Error"));
|
|
66
|
+
console.error(chalk.dim(err.message));
|
|
67
|
+
if (process.env.DEBUG && err.stack) {
|
|
68
|
+
console.error(chalk.dim("\nStack trace:"));
|
|
69
|
+
console.error(chalk.dim(err.stack));
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
console.error(chalk.dim("\nRun with DEBUG=1 environment variable for more details"));
|
|
73
|
+
}
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare class RelesioError extends Error {
|
|
2
|
+
code: string;
|
|
3
|
+
constructor(message: string, code: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class AuthenticationError extends RelesioError {
|
|
6
|
+
constructor(message?: string);
|
|
7
|
+
}
|
|
8
|
+
export declare class APIError extends RelesioError {
|
|
9
|
+
statusCode: number;
|
|
10
|
+
response?: unknown | undefined;
|
|
11
|
+
constructor(message: string, statusCode: number, response?: unknown | undefined);
|
|
12
|
+
}
|
|
13
|
+
export declare class ConfigError extends RelesioError {
|
|
14
|
+
constructor(message: string);
|
|
15
|
+
}
|
|
16
|
+
export declare class ValidationError extends RelesioError {
|
|
17
|
+
details?: unknown | undefined;
|
|
18
|
+
constructor(message: string, details?: unknown | undefined);
|
|
19
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export class RelesioError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
constructor(message, code) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.code = code;
|
|
6
|
+
this.name = "RelesioError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export class AuthenticationError extends RelesioError {
|
|
10
|
+
constructor(message = "Not authenticated. Run 'relesio auth login' first") {
|
|
11
|
+
super(message, "AUTH_ERROR");
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class APIError extends RelesioError {
|
|
15
|
+
statusCode;
|
|
16
|
+
response;
|
|
17
|
+
constructor(message, statusCode, response) {
|
|
18
|
+
super(message, "API_ERROR");
|
|
19
|
+
this.statusCode = statusCode;
|
|
20
|
+
this.response = response;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export class ConfigError extends RelesioError {
|
|
24
|
+
constructor(message) {
|
|
25
|
+
super(message, "CONFIG_ERROR");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export class ValidationError extends RelesioError {
|
|
29
|
+
details;
|
|
30
|
+
constructor(message, details) {
|
|
31
|
+
super(message, "VALIDATION_ERROR");
|
|
32
|
+
this.details = details;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface FileInfo {
|
|
2
|
+
path: string;
|
|
3
|
+
absolutePath: string;
|
|
4
|
+
size: number;
|
|
5
|
+
contentType: string;
|
|
6
|
+
checksum: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Scan directory recursively and collect file information
|
|
10
|
+
*/
|
|
11
|
+
export declare function scanDirectory(directoryPath: string, baseDir?: string): Promise<FileInfo[]>;
|
|
12
|
+
/**
|
|
13
|
+
* Calculate SHA-256 checksum for a file
|
|
14
|
+
*/
|
|
15
|
+
export declare function calculateChecksum(filePath: string): Promise<string>;
|
|
16
|
+
/**
|
|
17
|
+
* Format file size for display
|
|
18
|
+
*/
|
|
19
|
+
export declare function formatFileSize(bytes: number): string;
|
|
20
|
+
/**
|
|
21
|
+
* Get total size of files
|
|
22
|
+
*/
|
|
23
|
+
export declare function getTotalSize(files: FileInfo[]): number;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import * as crypto from "node:crypto";
|
|
4
|
+
import { lookup as mimeTypeLookup } from "mime-types";
|
|
5
|
+
/**
|
|
6
|
+
* Scan directory recursively and collect file information
|
|
7
|
+
*/
|
|
8
|
+
export async function scanDirectory(directoryPath, baseDir) {
|
|
9
|
+
const resolvedPath = path.resolve(directoryPath);
|
|
10
|
+
const base = baseDir || resolvedPath;
|
|
11
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
12
|
+
throw new Error(`Directory does not exist: ${directoryPath}`);
|
|
13
|
+
}
|
|
14
|
+
const stats = fs.statSync(resolvedPath);
|
|
15
|
+
if (!stats.isDirectory()) {
|
|
16
|
+
throw new Error(`Not a directory: ${directoryPath}`);
|
|
17
|
+
}
|
|
18
|
+
const files = [];
|
|
19
|
+
const entries = fs.readdirSync(resolvedPath);
|
|
20
|
+
for (const entry of entries) {
|
|
21
|
+
const fullPath = path.join(resolvedPath, entry);
|
|
22
|
+
const entryStat = fs.statSync(fullPath);
|
|
23
|
+
// Skip hidden files and common directories to ignore
|
|
24
|
+
if (entry.startsWith(".") ||
|
|
25
|
+
entry === "node_modules" ||
|
|
26
|
+
entry === "dist" ||
|
|
27
|
+
entry === "__tests__") {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (entryStat.isDirectory()) {
|
|
31
|
+
// Recursively scan subdirectory
|
|
32
|
+
const subFiles = await scanDirectory(fullPath, base);
|
|
33
|
+
files.push(...subFiles);
|
|
34
|
+
}
|
|
35
|
+
else if (entryStat.isFile()) {
|
|
36
|
+
// Calc relative path from base directory
|
|
37
|
+
const relativePath = path.relative(base, fullPath);
|
|
38
|
+
// Get content type
|
|
39
|
+
const contentType = mimeTypeLookup(fullPath) || "application/octet-stream";
|
|
40
|
+
// Calculate checksum
|
|
41
|
+
const checksum = await calculateChecksum(fullPath);
|
|
42
|
+
files.push({
|
|
43
|
+
path: relativePath.replace(/\\/g, "/"), // Normalize to forward slashes
|
|
44
|
+
absolutePath: fullPath,
|
|
45
|
+
size: entryStat.size,
|
|
46
|
+
contentType,
|
|
47
|
+
checksum
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return files;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Calculate SHA-256 checksum for a file
|
|
55
|
+
*/
|
|
56
|
+
export async function calculateChecksum(filePath) {
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
const hash = crypto.createHash("sha256");
|
|
59
|
+
const stream = fs.createReadStream(filePath);
|
|
60
|
+
stream.on("data", (data) => hash.update(data));
|
|
61
|
+
stream.on("end", () => resolve(hash.digest("hex")));
|
|
62
|
+
stream.on("error", reject);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Format file size for display
|
|
67
|
+
*/
|
|
68
|
+
export function formatFileSize(bytes) {
|
|
69
|
+
if (bytes === 0)
|
|
70
|
+
return "0 B";
|
|
71
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
72
|
+
const k = 1024;
|
|
73
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
74
|
+
return `${(bytes / k ** i).toFixed(2)} ${units[i]}`;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get total size of files
|
|
78
|
+
*/
|
|
79
|
+
export function getTotalSize(files) {
|
|
80
|
+
return files.reduce((sum, file) => sum + file.size, 0);
|
|
81
|
+
}
|