everything-dev 0.2.1 → 0.3.1
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/package.json +80 -79
- package/src/cli.ts +1491 -1198
- package/src/components/monitor-view.tsx +423 -419
- package/src/config.ts +529 -241
- package/src/contract.ts +381 -364
- package/src/lib/env.ts +83 -65
- package/src/lib/nova.ts +207 -195
- package/src/lib/orchestrator.ts +232 -199
- package/src/lib/process-registry.ts +141 -132
- package/src/lib/process.ts +499 -409
- package/src/lib/resource-monitor/diff.ts +27 -9
- package/src/lib/resource-monitor/platform/darwin.ts +31 -18
- package/src/lib/resource-monitor/snapshot.ts +164 -151
- package/src/plugin.ts +2281 -1841
- package/src/types.ts +182 -83
- package/src/ui/head.ts +37 -26
- package/src/utils/banner.ts +7 -9
- package/src/utils/run.ts +27 -16
- package/src/lib/secrets.ts +0 -29
package/src/lib/env.ts
CHANGED
|
@@ -2,90 +2,108 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { Effect } from "every-plugin/effect";
|
|
5
|
-
import {
|
|
5
|
+
import { getProjectRoot } from "../config";
|
|
6
6
|
|
|
7
7
|
export interface BosEnv {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
ZE_SERVER_TOKEN?: string;
|
|
9
|
+
ZE_USER_EMAIL?: string;
|
|
10
|
+
NEAR_PRIVATE_KEY?: string;
|
|
11
|
+
GATEWAY_PRIVATE_KEY?: string;
|
|
12
|
+
NOVA_SECRETS_CID?: string;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
function parseEnvFile(content: string): Record<string, string> {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
const result: Record<string, string> = {};
|
|
17
|
+
const lines = content.split("\n");
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
for (const line of lines) {
|
|
20
|
+
const trimmed = line.trim();
|
|
21
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
const eqIndex = trimmed.indexOf("=");
|
|
24
|
+
if (eqIndex === -1) continue;
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
27
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
if (
|
|
30
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
31
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
32
|
+
) {
|
|
33
|
+
value = value.slice(1, -1);
|
|
34
|
+
}
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
result[key] = value;
|
|
37
|
+
}
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
return result;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
export const loadBosEnv = Effect.gen(function* () {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
43
|
+
let configDir: string;
|
|
44
|
+
try {
|
|
45
|
+
configDir = getProjectRoot();
|
|
46
|
+
} catch {
|
|
47
|
+
configDir = process.cwd();
|
|
48
|
+
}
|
|
49
|
+
const envBosPath = path.join(configDir, ".env.bos");
|
|
50
|
+
const envPath = path.join(configDir, ".env");
|
|
51
|
+
|
|
52
|
+
let envVars: BosEnv = {};
|
|
53
|
+
|
|
54
|
+
const envFilePath = existsSync(envBosPath)
|
|
55
|
+
? envBosPath
|
|
56
|
+
: existsSync(envPath)
|
|
57
|
+
? envPath
|
|
58
|
+
: null;
|
|
59
|
+
|
|
60
|
+
if (envFilePath) {
|
|
61
|
+
const content = yield* Effect.tryPromise({
|
|
62
|
+
try: () => readFile(envFilePath, "utf-8"),
|
|
63
|
+
catch: () => new Error(`Failed to read ${envFilePath}`),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const parsed = parseEnvFile(content);
|
|
67
|
+
envVars = {
|
|
68
|
+
ZE_SERVER_TOKEN: parsed.ZE_SERVER_TOKEN,
|
|
69
|
+
ZE_USER_EMAIL: parsed.ZE_USER_EMAIL,
|
|
70
|
+
NEAR_PRIVATE_KEY: parsed.NEAR_PRIVATE_KEY,
|
|
71
|
+
GATEWAY_PRIVATE_KEY: parsed.GATEWAY_PRIVATE_KEY,
|
|
72
|
+
NOVA_SECRETS_CID: parsed.NOVA_SECRETS_CID,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
envVars.ZE_SERVER_TOKEN =
|
|
77
|
+
envVars.ZE_SERVER_TOKEN || process.env.ZE_SERVER_TOKEN;
|
|
78
|
+
envVars.ZE_USER_EMAIL = envVars.ZE_USER_EMAIL || process.env.ZE_USER_EMAIL;
|
|
79
|
+
envVars.NEAR_PRIVATE_KEY =
|
|
80
|
+
envVars.NEAR_PRIVATE_KEY || process.env.NEAR_PRIVATE_KEY;
|
|
81
|
+
envVars.GATEWAY_PRIVATE_KEY =
|
|
82
|
+
envVars.GATEWAY_PRIVATE_KEY || process.env.GATEWAY_PRIVATE_KEY;
|
|
83
|
+
envVars.NOVA_SECRETS_CID =
|
|
84
|
+
envVars.NOVA_SECRETS_CID || process.env.NOVA_SECRETS_CID;
|
|
85
|
+
|
|
86
|
+
return envVars;
|
|
72
87
|
});
|
|
73
88
|
|
|
74
|
-
export const ZEPHYR_DOCS_URL =
|
|
89
|
+
export const ZEPHYR_DOCS_URL =
|
|
90
|
+
"https://docs.zephyr-cloud.io/features/ci-cd-server-token";
|
|
75
91
|
|
|
76
92
|
export const getBuildEnv = (bosEnv: BosEnv): Record<string, string> => {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
93
|
+
const env: Record<string, string> = {
|
|
94
|
+
...(process.env as Record<string, string>),
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
if (bosEnv.ZE_SERVER_TOKEN) {
|
|
98
|
+
env.ZE_SERVER_TOKEN = bosEnv.ZE_SERVER_TOKEN;
|
|
99
|
+
}
|
|
100
|
+
if (bosEnv.ZE_USER_EMAIL) {
|
|
101
|
+
env.ZE_USER_EMAIL = bosEnv.ZE_USER_EMAIL;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return env;
|
|
87
105
|
};
|
|
88
106
|
|
|
89
107
|
export const hasZephyrConfig = (bosEnv: BosEnv): boolean => {
|
|
90
|
-
|
|
108
|
+
return !!(bosEnv.ZE_SERVER_TOKEN && bosEnv.ZE_USER_EMAIL);
|
|
91
109
|
};
|
package/src/lib/nova.ts
CHANGED
|
@@ -1,254 +1,266 @@
|
|
|
1
|
-
import { NovaSdk } from "nova-sdk-js";
|
|
2
1
|
import { Effect } from "every-plugin/effect";
|
|
3
|
-
import {
|
|
2
|
+
import { NovaSdk } from "nova-sdk-js";
|
|
3
|
+
import { getProjectRoot } from "../config";
|
|
4
4
|
|
|
5
5
|
export interface NovaConfig {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
accountId: string;
|
|
7
|
+
sessionToken: string;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export interface SecretsData {
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
secrets: Record<string, string>;
|
|
12
|
+
updatedAt: string;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export interface UploadResult {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
cid: string;
|
|
17
|
+
groupId: string;
|
|
18
|
+
txHash?: string;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export const getNovaConfig = Effect.gen(function* () {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
22
|
+
const accountId = process.env.NOVA_ACCOUNT_ID;
|
|
23
|
+
const sessionToken = process.env.NOVA_API_KEY;
|
|
24
|
+
|
|
25
|
+
if (!accountId || !sessionToken) {
|
|
26
|
+
return yield* Effect.fail(
|
|
27
|
+
new Error(
|
|
28
|
+
"NOVA credentials not configured. Run 'bos login' to authenticate with NOVA.",
|
|
29
|
+
),
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return { accountId, sessionToken } satisfies NovaConfig;
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
export function createNovaClient(config: NovaConfig): NovaSdk {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
return new NovaSdk(config.accountId, {
|
|
38
|
+
apiKey: config.sessionToken,
|
|
39
|
+
});
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export function getSecretsGroupId(nearAccount: string): string {
|
|
43
|
-
|
|
43
|
+
return `${nearAccount}-secrets`;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
export const registerSecretsGroup = (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
nova: NovaSdk,
|
|
48
|
+
nearAccount: string,
|
|
49
|
+
novaAccount: string,
|
|
50
50
|
) =>
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
Effect.gen(function* () {
|
|
52
|
+
const groupId = getSecretsGroupId(nearAccount);
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
yield* Effect.tryPromise({
|
|
55
|
+
try: () => nova.registerGroup(groupId),
|
|
56
|
+
catch: (e) => new Error(`Failed to register NOVA group: ${e}`),
|
|
57
|
+
});
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
yield* Effect.tryPromise({
|
|
60
|
+
try: () => nova.addGroupMember(groupId, novaAccount),
|
|
61
|
+
catch: (e) =>
|
|
62
|
+
new Error(`Failed to add gateway Nova account to group: ${e}`),
|
|
63
|
+
});
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
return groupId;
|
|
66
|
+
});
|
|
66
67
|
|
|
67
68
|
export const uploadSecrets = (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
nova: NovaSdk,
|
|
70
|
+
groupId: string,
|
|
71
|
+
secrets: Record<string, string>,
|
|
71
72
|
) =>
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
73
|
+
Effect.gen(function* () {
|
|
74
|
+
const secretsData: SecretsData = {
|
|
75
|
+
secrets,
|
|
76
|
+
updatedAt: new Date().toISOString(),
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const buffer = Buffer.from(JSON.stringify(secretsData, null, 2));
|
|
80
|
+
|
|
81
|
+
const result = yield* Effect.tryPromise({
|
|
82
|
+
try: () => nova.upload(groupId, buffer, "secrets.json"),
|
|
83
|
+
catch: (e) => new Error(`Failed to upload secrets to NOVA: ${e}`),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
cid: result.cid,
|
|
88
|
+
groupId,
|
|
89
|
+
txHash: result.trans_id,
|
|
90
|
+
} satisfies UploadResult;
|
|
91
|
+
});
|
|
91
92
|
|
|
92
93
|
export const retrieveSecrets = (nova: NovaSdk, groupId: string, cid: string) =>
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
Effect.gen(function* () {
|
|
95
|
+
const result = yield* Effect.tryPromise({
|
|
96
|
+
try: () => nova.retrieve(groupId, cid),
|
|
97
|
+
catch: (e) => new Error(`Failed to retrieve secrets from NOVA: ${e}`),
|
|
98
|
+
});
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
const secretsData = JSON.parse(result.data.toString()) as SecretsData;
|
|
101
|
+
return secretsData;
|
|
102
|
+
});
|
|
102
103
|
|
|
103
104
|
export function parseEnvFile(content: string): Record<string, string> {
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
const secrets: Record<string, string> = {};
|
|
106
|
+
const lines = content.split("\n");
|
|
106
107
|
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
for (const line of lines) {
|
|
109
|
+
const trimmed = line.trim();
|
|
109
110
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
113
114
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
const eqIndex = trimmed.indexOf("=");
|
|
116
|
+
if (eqIndex === -1) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
118
119
|
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
121
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
121
122
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
123
|
+
if (
|
|
124
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
125
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
126
|
+
) {
|
|
127
|
+
value = value.slice(1, -1);
|
|
128
|
+
}
|
|
128
129
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
if (key) {
|
|
131
|
+
secrets[key] = value;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
133
134
|
|
|
134
|
-
|
|
135
|
+
return secrets;
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
export function filterSecretsToRequired(
|
|
138
|
-
|
|
139
|
-
|
|
139
|
+
allSecrets: Record<string, string>,
|
|
140
|
+
requiredKeys: string[],
|
|
140
141
|
): Record<string, string> {
|
|
141
|
-
|
|
142
|
+
const filtered: Record<string, string> = {};
|
|
142
143
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
144
|
+
for (const key of requiredKeys) {
|
|
145
|
+
if (key in allSecrets) {
|
|
146
|
+
filtered[key] = allSecrets[key];
|
|
147
|
+
}
|
|
148
|
+
}
|
|
148
149
|
|
|
149
|
-
|
|
150
|
+
return filtered;
|
|
150
151
|
}
|
|
151
152
|
|
|
152
153
|
export function hasNovaCredentials(): boolean {
|
|
153
|
-
|
|
154
|
+
return !!(process.env.NOVA_ACCOUNT_ID && process.env.NOVA_API_KEY);
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
function getBosEnvPath(): string {
|
|
157
|
-
|
|
158
|
+
try {
|
|
159
|
+
return `${getProjectRoot()}/.env.bos`;
|
|
160
|
+
} catch {
|
|
161
|
+
// Fallback to cwd if config not loaded
|
|
162
|
+
return `${process.cwd()}/.env.bos`;
|
|
163
|
+
}
|
|
158
164
|
}
|
|
159
165
|
|
|
160
166
|
export const saveNovaCredentials = (accountId: string, sessionToken: string) =>
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
167
|
+
Effect.gen(function* () {
|
|
168
|
+
const envPath = getBosEnvPath();
|
|
169
|
+
let content = "";
|
|
170
|
+
|
|
171
|
+
const file = Bun.file(envPath);
|
|
172
|
+
const exists = yield* Effect.promise(() => file.exists());
|
|
173
|
+
|
|
174
|
+
if (exists) {
|
|
175
|
+
content = yield* Effect.tryPromise({
|
|
176
|
+
try: () => file.text(),
|
|
177
|
+
catch: () => new Error("File read failed"),
|
|
178
|
+
}).pipe(Effect.orElseSucceed(() => ""));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const lines = content.split("\n");
|
|
182
|
+
const newLines: string[] = [];
|
|
183
|
+
let foundAccountId = false;
|
|
184
|
+
let foundSessionToken = false;
|
|
185
|
+
|
|
186
|
+
for (const line of lines) {
|
|
187
|
+
const trimmed = line.trim();
|
|
188
|
+
if (trimmed.startsWith("NOVA_ACCOUNT_ID=")) {
|
|
189
|
+
newLines.push(`NOVA_ACCOUNT_ID=${accountId}`);
|
|
190
|
+
foundAccountId = true;
|
|
191
|
+
} else if (trimmed.startsWith("NOVA_API_KEY=")) {
|
|
192
|
+
newLines.push(`NOVA_API_KEY=${sessionToken}`);
|
|
193
|
+
foundSessionToken = true;
|
|
194
|
+
} else {
|
|
195
|
+
newLines.push(line);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (!foundAccountId) {
|
|
200
|
+
if (newLines.length > 0 && newLines[newLines.length - 1] !== "") {
|
|
201
|
+
newLines.push("");
|
|
202
|
+
}
|
|
203
|
+
newLines.push(`NOVA_ACCOUNT_ID=${accountId}`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (!foundSessionToken) {
|
|
207
|
+
newLines.push(`NOVA_API_KEY=${sessionToken}`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
yield* Effect.tryPromise({
|
|
211
|
+
try: () => Bun.write(envPath, newLines.join("\n")),
|
|
212
|
+
catch: (e) => new Error(`Failed to save credentials: ${e}`),
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
process.env.NOVA_ACCOUNT_ID = accountId;
|
|
216
|
+
process.env.NOVA_API_KEY = sessionToken;
|
|
217
|
+
});
|
|
212
218
|
|
|
213
219
|
export const removeNovaCredentials = Effect.gen(function* () {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
220
|
+
const envPath = getBosEnvPath();
|
|
221
|
+
let content = "";
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
content = yield* Effect.tryPromise({
|
|
225
|
+
try: () => Bun.file(envPath).text(),
|
|
226
|
+
catch: () => "",
|
|
227
|
+
});
|
|
228
|
+
} catch {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const lines = content.split("\n");
|
|
233
|
+
const newLines = lines.filter((line) => {
|
|
234
|
+
const trimmed = line.trim();
|
|
235
|
+
return (
|
|
236
|
+
!trimmed.startsWith("NOVA_ACCOUNT_ID=") &&
|
|
237
|
+
!trimmed.startsWith("NOVA_API_KEY=")
|
|
238
|
+
);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
yield* Effect.tryPromise({
|
|
242
|
+
try: () => Bun.write(envPath, newLines.join("\n")),
|
|
243
|
+
catch: (e) => new Error(`Failed to remove credentials: ${e}`),
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
delete process.env.NOVA_ACCOUNT_ID;
|
|
247
|
+
delete process.env.NOVA_API_KEY;
|
|
239
248
|
});
|
|
240
249
|
|
|
241
|
-
export const verifyNovaCredentials = (
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
250
|
+
export const verifyNovaCredentials = (
|
|
251
|
+
accountId: string,
|
|
252
|
+
sessionToken: string,
|
|
253
|
+
) =>
|
|
254
|
+
Effect.gen(function* () {
|
|
255
|
+
const nova = new NovaSdk(accountId, { apiKey: sessionToken });
|
|
256
|
+
|
|
257
|
+
yield* Effect.tryPromise({
|
|
258
|
+
try: () => nova.getBalance(),
|
|
259
|
+
catch: (e) => {
|
|
260
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
261
|
+
return new Error(`NOVA verification failed: ${message}`);
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
return true;
|
|
266
|
+
});
|