newo 1.3.0 → 1.5.0
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/.env.example +2 -2
- package/CHANGELOG.md +99 -2
- package/README.md +59 -10
- package/dist/akb.d.ts +10 -0
- package/dist/akb.js +84 -0
- package/dist/api.d.ts +13 -0
- package/dist/api.js +100 -0
- package/dist/auth.d.ts +6 -0
- package/dist/auth.js +104 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +111 -0
- package/dist/fsutil.d.ts +12 -0
- package/dist/fsutil.js +28 -0
- package/dist/hash.d.ts +5 -0
- package/dist/hash.js +17 -0
- package/dist/sync.d.ts +7 -0
- package/dist/sync.js +337 -0
- package/dist/types.d.ts +206 -0
- package/dist/types.js +5 -0
- package/package.json +32 -9
- package/src/{akb.js → akb.ts} +16 -25
- package/src/api.ts +127 -0
- package/src/auth.ts +142 -0
- package/src/{cli.js → cli.ts} +29 -15
- package/src/fsutil.ts +41 -0
- package/src/hash.ts +20 -0
- package/src/sync.ts +396 -0
- package/src/types.ts +248 -0
- package/src/api.js +0 -98
- package/src/auth.js +0 -92
- package/src/fsutil.js +0 -26
- package/src/hash.js +0 -17
- package/src/sync.js +0 -284
package/src/types.ts
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comprehensive type definitions for NEWO CLI
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface NewoEnvironment {
|
|
6
|
+
NEWO_BASE_URL?: string;
|
|
7
|
+
NEWO_PROJECT_ID?: string;
|
|
8
|
+
NEWO_API_KEY?: string;
|
|
9
|
+
NEWO_ACCESS_TOKEN?: string;
|
|
10
|
+
NEWO_REFRESH_TOKEN?: string;
|
|
11
|
+
NEWO_REFRESH_URL?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Authentication Types
|
|
15
|
+
export interface TokenResponse {
|
|
16
|
+
access_token?: string;
|
|
17
|
+
token?: string;
|
|
18
|
+
accessToken?: string;
|
|
19
|
+
refresh_token?: string;
|
|
20
|
+
refreshToken?: string;
|
|
21
|
+
expires_in?: number;
|
|
22
|
+
expiresIn?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface StoredTokens {
|
|
26
|
+
access_token: string;
|
|
27
|
+
refresh_token: string;
|
|
28
|
+
expires_at: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// API Response Types
|
|
32
|
+
export interface ProjectMeta {
|
|
33
|
+
id: string;
|
|
34
|
+
idn: string;
|
|
35
|
+
title: string;
|
|
36
|
+
description?: string;
|
|
37
|
+
created_at?: string;
|
|
38
|
+
updated_at?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface Agent {
|
|
42
|
+
id: string;
|
|
43
|
+
idn: string;
|
|
44
|
+
title?: string;
|
|
45
|
+
description?: string;
|
|
46
|
+
flows?: Flow[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface Flow {
|
|
50
|
+
id: string;
|
|
51
|
+
idn: string;
|
|
52
|
+
title: string;
|
|
53
|
+
description?: string;
|
|
54
|
+
default_runner_type: RunnerType;
|
|
55
|
+
default_model: ModelConfig;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface ModelConfig {
|
|
59
|
+
model_idn: string;
|
|
60
|
+
provider_idn: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface SkillParameter {
|
|
64
|
+
name: string;
|
|
65
|
+
default_value?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface Skill {
|
|
69
|
+
id: string;
|
|
70
|
+
idn: string;
|
|
71
|
+
title: string;
|
|
72
|
+
prompt_script?: string;
|
|
73
|
+
runner_type: RunnerType;
|
|
74
|
+
model: ModelConfig;
|
|
75
|
+
parameters: SkillParameter[];
|
|
76
|
+
path?: string | undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface FlowEvent {
|
|
80
|
+
id: string;
|
|
81
|
+
idn: string;
|
|
82
|
+
description: string;
|
|
83
|
+
skill_selector: SkillSelector;
|
|
84
|
+
skill_idn?: string;
|
|
85
|
+
state_idn?: string;
|
|
86
|
+
integration_idn?: string;
|
|
87
|
+
connector_idn?: string;
|
|
88
|
+
interrupt_mode: InterruptMode;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface FlowState {
|
|
92
|
+
id: string;
|
|
93
|
+
idn: string;
|
|
94
|
+
title: string;
|
|
95
|
+
default_value?: string;
|
|
96
|
+
scope: StateFieldScope;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Enum Types
|
|
100
|
+
export type RunnerType = 'guidance' | 'nsl';
|
|
101
|
+
export type SkillSelector = 'first' | 'last' | 'random' | 'all';
|
|
102
|
+
export type InterruptMode = 'allow' | 'deny' | 'queue';
|
|
103
|
+
export type StateFieldScope = 'flow' | 'agent' | 'project' | 'global';
|
|
104
|
+
|
|
105
|
+
// File System Types
|
|
106
|
+
export interface SkillMetadata {
|
|
107
|
+
id: string;
|
|
108
|
+
title: string;
|
|
109
|
+
idn: string;
|
|
110
|
+
runner_type: RunnerType;
|
|
111
|
+
model: ModelConfig;
|
|
112
|
+
parameters: SkillParameter[];
|
|
113
|
+
path?: string | undefined;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface FlowData {
|
|
117
|
+
id: string;
|
|
118
|
+
skills: Record<string, SkillMetadata>;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface AgentData {
|
|
122
|
+
id: string;
|
|
123
|
+
flows: Record<string, FlowData>;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface ProjectData {
|
|
127
|
+
projectId: string;
|
|
128
|
+
projectIdn: string;
|
|
129
|
+
agents: Record<string, AgentData>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface ProjectMap {
|
|
133
|
+
projects: Record<string, ProjectData>;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Legacy single-project format support
|
|
137
|
+
export interface LegacyProjectMap extends ProjectData {
|
|
138
|
+
projects?: Record<string, ProjectData>;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface HashStore {
|
|
142
|
+
[filePath: string]: string;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// AKB Types
|
|
146
|
+
export interface ParsedArticle {
|
|
147
|
+
topic_name: string;
|
|
148
|
+
persona_id: string | null;
|
|
149
|
+
topic_summary: string;
|
|
150
|
+
topic_facts: string[];
|
|
151
|
+
confidence: number;
|
|
152
|
+
source: string;
|
|
153
|
+
labels: string[];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface AkbImportArticle extends Omit<ParsedArticle, 'persona_id'> {
|
|
157
|
+
persona_id: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// CLI Types
|
|
161
|
+
export interface CliArgs {
|
|
162
|
+
_: string[];
|
|
163
|
+
verbose?: boolean;
|
|
164
|
+
v?: boolean;
|
|
165
|
+
[key: string]: unknown;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// flows.yaml Generation Types
|
|
169
|
+
export interface FlowsYamlSkill {
|
|
170
|
+
idn: string;
|
|
171
|
+
title: string;
|
|
172
|
+
prompt_script: string;
|
|
173
|
+
runner_type: string;
|
|
174
|
+
model: ModelConfig;
|
|
175
|
+
parameters: Array<{
|
|
176
|
+
name: string;
|
|
177
|
+
default_value: string;
|
|
178
|
+
}>;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export interface FlowsYamlEvent {
|
|
182
|
+
title: string;
|
|
183
|
+
idn: string;
|
|
184
|
+
skill_selector: string;
|
|
185
|
+
skill_idn?: string | undefined;
|
|
186
|
+
state_idn?: string | undefined;
|
|
187
|
+
integration_idn?: string | undefined;
|
|
188
|
+
connector_idn?: string | undefined;
|
|
189
|
+
interrupt_mode: string;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export interface FlowsYamlState {
|
|
193
|
+
title: string;
|
|
194
|
+
idn: string;
|
|
195
|
+
default_value?: string | undefined;
|
|
196
|
+
scope: string;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export interface FlowsYamlFlow {
|
|
200
|
+
idn: string;
|
|
201
|
+
title: string;
|
|
202
|
+
description: string | null;
|
|
203
|
+
default_runner_type: string;
|
|
204
|
+
default_provider_idn: string;
|
|
205
|
+
default_model_idn: string;
|
|
206
|
+
skills: FlowsYamlSkill[];
|
|
207
|
+
events: FlowsYamlEvent[];
|
|
208
|
+
state_fields: FlowsYamlState[];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export interface FlowsYamlAgent {
|
|
212
|
+
agent_idn: string;
|
|
213
|
+
agent_description?: string | undefined;
|
|
214
|
+
agent_flows: FlowsYamlFlow[];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface FlowsYamlData {
|
|
218
|
+
flows: FlowsYamlAgent[];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// HTTP Client Types
|
|
222
|
+
export interface AxiosClientConfig {
|
|
223
|
+
baseURL?: string;
|
|
224
|
+
headers?: Record<string, string>;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Error Types
|
|
228
|
+
export interface NewoApiError extends Error {
|
|
229
|
+
response?: {
|
|
230
|
+
status: number;
|
|
231
|
+
data: unknown;
|
|
232
|
+
};
|
|
233
|
+
config?: {
|
|
234
|
+
method?: string;
|
|
235
|
+
url?: string;
|
|
236
|
+
headers?: Record<string, string>;
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Status Types
|
|
241
|
+
export type FileStatus = 'M' | 'D' | 'clean';
|
|
242
|
+
|
|
243
|
+
export interface StatusResult {
|
|
244
|
+
filePath: string;
|
|
245
|
+
status: FileStatus;
|
|
246
|
+
oldHash?: string;
|
|
247
|
+
newHash?: string;
|
|
248
|
+
}
|
package/src/api.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
|
-
import dotenv from 'dotenv';
|
|
3
|
-
import { getValidAccessToken, forceReauth } from './auth.js';
|
|
4
|
-
dotenv.config();
|
|
5
|
-
|
|
6
|
-
const { NEWO_BASE_URL } = process.env;
|
|
7
|
-
|
|
8
|
-
export async function makeClient(verbose = false) {
|
|
9
|
-
let accessToken = await getValidAccessToken();
|
|
10
|
-
if (verbose) console.log('✓ Access token obtained');
|
|
11
|
-
|
|
12
|
-
const client = axios.create({
|
|
13
|
-
baseURL: NEWO_BASE_URL,
|
|
14
|
-
headers: { accept: 'application/json' }
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
client.interceptors.request.use(async (config) => {
|
|
18
|
-
config.headers = config.headers || {};
|
|
19
|
-
config.headers.Authorization = `Bearer ${accessToken}`;
|
|
20
|
-
if (verbose) {
|
|
21
|
-
console.log(`→ ${config.method?.toUpperCase()} ${config.url}`);
|
|
22
|
-
if (config.data) console.log(' Data:', JSON.stringify(config.data, null, 2));
|
|
23
|
-
if (config.params) console.log(' Params:', config.params);
|
|
24
|
-
}
|
|
25
|
-
return config;
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
let retried = false;
|
|
29
|
-
client.interceptors.response.use(
|
|
30
|
-
r => {
|
|
31
|
-
if (verbose) {
|
|
32
|
-
console.log(`← ${r.status} ${r.config.method?.toUpperCase()} ${r.config.url}`);
|
|
33
|
-
if (r.data && Object.keys(r.data).length < 20) {
|
|
34
|
-
console.log(' Response:', JSON.stringify(r.data, null, 2));
|
|
35
|
-
} else if (r.data) {
|
|
36
|
-
console.log(` Response: [${typeof r.data}] ${Array.isArray(r.data) ? r.data.length + ' items' : 'large object'}`);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return r;
|
|
40
|
-
},
|
|
41
|
-
async (error) => {
|
|
42
|
-
const status = error?.response?.status;
|
|
43
|
-
if (verbose) {
|
|
44
|
-
console.log(`← ${status} ${error.config?.method?.toUpperCase()} ${error.config?.url} - ${error.message}`);
|
|
45
|
-
if (error.response?.data) console.log(' Error data:', error.response.data);
|
|
46
|
-
}
|
|
47
|
-
if (status === 401 && !retried) {
|
|
48
|
-
retried = true;
|
|
49
|
-
if (verbose) console.log('🔄 Retrying with fresh token...');
|
|
50
|
-
accessToken = await forceReauth();
|
|
51
|
-
error.config.headers.Authorization = `Bearer ${accessToken}`;
|
|
52
|
-
return client.request(error.config);
|
|
53
|
-
}
|
|
54
|
-
throw error;
|
|
55
|
-
}
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
return client;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export async function listAgents(client, projectId) {
|
|
62
|
-
const r = await client.get(`/api/v1/bff/agents/list`, { params: { project_id: projectId } });
|
|
63
|
-
return r.data;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export async function getProjectMeta(client, projectId) {
|
|
67
|
-
const r = await client.get(`/api/v1/designer/projects/by-id/${projectId}`);
|
|
68
|
-
return r.data;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export async function listFlowSkills(client, flowId) {
|
|
72
|
-
const r = await client.get(`/api/v1/designer/flows/${flowId}/skills`);
|
|
73
|
-
return r.data;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export async function getSkill(client, skillId) {
|
|
77
|
-
const r = await client.get(`/api/v1/designer/skills/${skillId}`);
|
|
78
|
-
return r.data;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export async function updateSkill(client, skillObject) {
|
|
82
|
-
await client.put(`/api/v1/designer/flows/skills/${skillObject.id}`, skillObject);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export async function listFlowEvents(client, flowId) {
|
|
86
|
-
const r = await client.get(`/api/v1/designer/flows/${flowId}/events`);
|
|
87
|
-
return r.data;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export async function listFlowStates(client, flowId) {
|
|
91
|
-
const r = await client.get(`/api/v1/designer/flows/${flowId}/states`);
|
|
92
|
-
return r.data;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export async function importAkbArticle(client, articleData) {
|
|
96
|
-
const r = await client.post('/api/v1/akb/append-manual', articleData);
|
|
97
|
-
return r.data;
|
|
98
|
-
}
|
package/src/auth.js
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import axios from 'axios';
|
|
4
|
-
import dotenv from 'dotenv';
|
|
5
|
-
dotenv.config();
|
|
6
|
-
|
|
7
|
-
const {
|
|
8
|
-
NEWO_BASE_URL,
|
|
9
|
-
NEWO_API_KEY,
|
|
10
|
-
NEWO_ACCESS_TOKEN,
|
|
11
|
-
NEWO_REFRESH_TOKEN,
|
|
12
|
-
NEWO_REFRESH_URL
|
|
13
|
-
} = process.env;
|
|
14
|
-
|
|
15
|
-
const STATE_DIR = path.join(process.cwd(), '.newo');
|
|
16
|
-
const TOKENS_PATH = path.join(STATE_DIR, 'tokens.json');
|
|
17
|
-
|
|
18
|
-
async function saveTokens(tokens) {
|
|
19
|
-
await fs.ensureDir(STATE_DIR);
|
|
20
|
-
await fs.writeJson(TOKENS_PATH, tokens, { spaces: 2 });
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async function loadTokens() {
|
|
24
|
-
if (await fs.pathExists(TOKENS_PATH)) {
|
|
25
|
-
return fs.readJson(TOKENS_PATH);
|
|
26
|
-
}
|
|
27
|
-
if (NEWO_ACCESS_TOKEN || NEWO_REFRESH_TOKEN) {
|
|
28
|
-
const t = {
|
|
29
|
-
access_token: NEWO_ACCESS_TOKEN || '',
|
|
30
|
-
refresh_token: NEWO_REFRESH_TOKEN || '',
|
|
31
|
-
expires_at: Date.now() + 10 * 60 * 1000
|
|
32
|
-
};
|
|
33
|
-
await saveTokens(t);
|
|
34
|
-
return t;
|
|
35
|
-
}
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function isExpired(tokens) {
|
|
40
|
-
if (!tokens?.expires_at) return false;
|
|
41
|
-
return Date.now() >= tokens.expires_at - 10_000;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export async function exchangeApiKeyForToken() {
|
|
45
|
-
if (!NEWO_API_KEY) throw new Error('NEWO_API_KEY not set. Provide an API key in .env');
|
|
46
|
-
const url = `${NEWO_BASE_URL}/api/v1/auth/api-key/token`;
|
|
47
|
-
const res = await axios.post(url, {}, { headers: { 'x-api-key': NEWO_API_KEY, 'accept': 'application/json' } });
|
|
48
|
-
const data = res.data || {};
|
|
49
|
-
const access = data.access_token || data.token || data.accessToken;
|
|
50
|
-
const refresh = data.refresh_token || data.refreshToken || '';
|
|
51
|
-
const expiresInSec = data.expires_in || data.expiresIn || 3600;
|
|
52
|
-
const tokens = { access_token: access, refresh_token: refresh, expires_at: Date.now() + expiresInSec * 1000 };
|
|
53
|
-
await saveTokens(tokens);
|
|
54
|
-
return tokens;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export async function refreshWithEndpoint(refreshToken) {
|
|
58
|
-
if (!NEWO_REFRESH_URL) throw new Error('NEWO_REFRESH_URL not set');
|
|
59
|
-
const res = await axios.post(NEWO_REFRESH_URL, { refresh_token: refreshToken }, { headers: { 'accept': 'application/json' } });
|
|
60
|
-
const data = res.data || {};
|
|
61
|
-
const access = data.access_token || data.token || data.accessToken;
|
|
62
|
-
const refresh = data.refresh_token ?? refreshToken;
|
|
63
|
-
const expiresInSec = data.expires_in || 3600;
|
|
64
|
-
const tokens = { access_token: access, refresh_token: refresh, expires_at: Date.now() + expiresInSec * 1000 };
|
|
65
|
-
await saveTokens(tokens);
|
|
66
|
-
return tokens;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export async function getValidAccessToken() {
|
|
70
|
-
let tokens = await loadTokens();
|
|
71
|
-
if (!tokens || !tokens.access_token) {
|
|
72
|
-
tokens = await exchangeApiKeyForToken();
|
|
73
|
-
return tokens.access_token;
|
|
74
|
-
}
|
|
75
|
-
if (!isExpired(tokens)) return tokens.access_token;
|
|
76
|
-
|
|
77
|
-
if (NEWO_REFRESH_URL && tokens.refresh_token) {
|
|
78
|
-
try {
|
|
79
|
-
tokens = await refreshWithEndpoint(tokens.refresh_token);
|
|
80
|
-
return tokens.access_token;
|
|
81
|
-
} catch (e) {
|
|
82
|
-
console.warn('Refresh failed, falling back to API key exchange…');
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
tokens = await exchangeApiKeyForToken();
|
|
86
|
-
return tokens.access_token;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export async function forceReauth() {
|
|
90
|
-
const tokens = await exchangeApiKeyForToken();
|
|
91
|
-
return tokens.access_token;
|
|
92
|
-
}
|
package/src/fsutil.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
|
|
4
|
-
export const ROOT_DIR = path.join(process.cwd(), 'project');
|
|
5
|
-
export const STATE_DIR = path.join(process.cwd(), '.newo');
|
|
6
|
-
export const MAP_PATH = path.join(STATE_DIR, 'map.json');
|
|
7
|
-
export const HASHES_PATH = path.join(STATE_DIR, 'hashes.json');
|
|
8
|
-
|
|
9
|
-
export async function ensureState() {
|
|
10
|
-
await fs.ensureDir(STATE_DIR);
|
|
11
|
-
await fs.ensureDir(ROOT_DIR);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function skillPath(agentIdn, flowIdn, skillIdn, runnerType = 'guidance') {
|
|
15
|
-
const extension = runnerType === 'nsl' ? '.jinja' : '.guidance';
|
|
16
|
-
return path.join(ROOT_DIR, agentIdn, flowIdn, `${skillIdn}${extension}`);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export async function writeFileAtomic(filepath, content) {
|
|
20
|
-
await fs.ensureDir(path.dirname(filepath));
|
|
21
|
-
await fs.writeFile(filepath, content, 'utf8');
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export async function readIfExists(filepath) {
|
|
25
|
-
return (await fs.pathExists(filepath)) ? fs.readFile(filepath, 'utf8') : null;
|
|
26
|
-
}
|
package/src/hash.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import crypto from 'crypto';
|
|
2
|
-
import fs from 'fs-extra';
|
|
3
|
-
import { ensureState, HASHES_PATH } from './fsutil.js';
|
|
4
|
-
|
|
5
|
-
export function sha256(str) {
|
|
6
|
-
return crypto.createHash('sha256').update(str, 'utf8').digest('hex');
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export async function loadHashes() {
|
|
10
|
-
await ensureState();
|
|
11
|
-
if (await fs.pathExists(HASHES_PATH)) return fs.readJson(HASHES_PATH);
|
|
12
|
-
return {};
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export async function saveHashes(h) {
|
|
16
|
-
await fs.writeJson(HASHES_PATH, h, { spaces: 2 });
|
|
17
|
-
}
|