newo 1.4.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/dist/sync.js ADDED
@@ -0,0 +1,337 @@
1
+ import { listProjects, listAgents, listFlowSkills, updateSkill, listFlowEvents, listFlowStates, getProjectMeta } from './api.js';
2
+ import { ensureState, skillPath, writeFileAtomic, readIfExists, MAP_PATH, projectDir, metadataPath } from './fsutil.js';
3
+ import fs from 'fs-extra';
4
+ import { sha256, loadHashes, saveHashes } from './hash.js';
5
+ import yaml from 'js-yaml';
6
+ import path from 'path';
7
+ export async function pullSingleProject(client, projectId, projectIdn, verbose = false) {
8
+ if (verbose)
9
+ console.log(`🔍 Fetching agents for project ${projectId} (${projectIdn})...`);
10
+ const agents = await listAgents(client, projectId);
11
+ if (verbose)
12
+ console.log(`📦 Found ${agents.length} agents`);
13
+ // Get and save project metadata
14
+ const projectMeta = await getProjectMeta(client, projectId);
15
+ await writeFileAtomic(metadataPath(projectIdn), JSON.stringify(projectMeta, null, 2));
16
+ if (verbose)
17
+ console.log(`✓ Saved metadata for ${projectIdn}`);
18
+ const projectMap = { projectId, projectIdn, agents: {} };
19
+ for (const agent of agents) {
20
+ const aKey = agent.idn;
21
+ projectMap.agents[aKey] = { id: agent.id, flows: {} };
22
+ for (const flow of agent.flows ?? []) {
23
+ projectMap.agents[aKey].flows[flow.idn] = { id: flow.id, skills: {} };
24
+ const skills = await listFlowSkills(client, flow.id);
25
+ for (const skill of skills) {
26
+ const file = skillPath(projectIdn, agent.idn, flow.idn, skill.idn, skill.runner_type);
27
+ await writeFileAtomic(file, skill.prompt_script || '');
28
+ // Store complete skill metadata for push operations
29
+ projectMap.agents[aKey].flows[flow.idn].skills[skill.idn] = {
30
+ id: skill.id,
31
+ title: skill.title,
32
+ idn: skill.idn,
33
+ runner_type: skill.runner_type,
34
+ model: skill.model,
35
+ parameters: skill.parameters,
36
+ path: skill.path || undefined
37
+ };
38
+ console.log(`✓ Pulled ${file}`);
39
+ }
40
+ }
41
+ }
42
+ // Generate flows.yaml for this project
43
+ if (verbose)
44
+ console.log(`📄 Generating flows.yaml for ${projectIdn}...`);
45
+ await generateFlowsYaml(client, agents, projectIdn, verbose);
46
+ return projectMap;
47
+ }
48
+ export async function pullAll(client, projectId = null, verbose = false) {
49
+ await ensureState();
50
+ if (projectId) {
51
+ // Single project mode
52
+ const projectMeta = await getProjectMeta(client, projectId);
53
+ const projectMap = await pullSingleProject(client, projectId, projectMeta.idn, verbose);
54
+ const idMap = { projects: { [projectMeta.idn]: projectMap } };
55
+ await fs.writeJson(MAP_PATH, idMap, { spaces: 2 });
56
+ // Generate hash tracking for this project
57
+ const hashes = {};
58
+ for (const [agentIdn, agentObj] of Object.entries(projectMap.agents)) {
59
+ for (const [flowIdn, flowObj] of Object.entries(agentObj.flows)) {
60
+ for (const [skillIdn, skillMeta] of Object.entries(flowObj.skills)) {
61
+ const p = skillPath(projectMeta.idn, agentIdn, flowIdn, skillIdn, skillMeta.runner_type);
62
+ const content = await fs.readFile(p, 'utf8');
63
+ hashes[p] = sha256(content);
64
+ }
65
+ }
66
+ }
67
+ await saveHashes(hashes);
68
+ return;
69
+ }
70
+ // Multi-project mode
71
+ if (verbose)
72
+ console.log('🔍 Fetching all projects...');
73
+ const projects = await listProjects(client);
74
+ if (verbose)
75
+ console.log(`📦 Found ${projects.length} projects`);
76
+ const idMap = { projects: {} };
77
+ const allHashes = {};
78
+ for (const project of projects) {
79
+ if (verbose)
80
+ console.log(`\n📁 Processing project: ${project.idn} (${project.title})`);
81
+ const projectMap = await pullSingleProject(client, project.id, project.idn, verbose);
82
+ idMap.projects[project.idn] = projectMap;
83
+ // Collect hashes for this project
84
+ for (const [agentIdn, agentObj] of Object.entries(projectMap.agents)) {
85
+ for (const [flowIdn, flowObj] of Object.entries(agentObj.flows)) {
86
+ for (const [skillIdn, skillMeta] of Object.entries(flowObj.skills)) {
87
+ const p = skillPath(project.idn, agentIdn, flowIdn, skillIdn, skillMeta.runner_type);
88
+ const content = await fs.readFile(p, 'utf8');
89
+ allHashes[p] = sha256(content);
90
+ }
91
+ }
92
+ }
93
+ }
94
+ await fs.writeJson(MAP_PATH, idMap, { spaces: 2 });
95
+ await saveHashes(allHashes);
96
+ }
97
+ export async function pushChanged(client, verbose = false) {
98
+ await ensureState();
99
+ if (!(await fs.pathExists(MAP_PATH))) {
100
+ throw new Error('Missing .newo/map.json. Run `newo pull` first.');
101
+ }
102
+ if (verbose)
103
+ console.log('📋 Loading project mapping...');
104
+ const idMap = await fs.readJson(MAP_PATH);
105
+ if (verbose)
106
+ console.log('🔍 Loading file hashes...');
107
+ const oldHashes = await loadHashes();
108
+ const newHashes = { ...oldHashes };
109
+ if (verbose)
110
+ console.log('🔄 Scanning for changes...');
111
+ let pushed = 0;
112
+ let scanned = 0;
113
+ // Handle both old single-project format and new multi-project format
114
+ const projects = 'projects' in idMap && idMap.projects ? idMap.projects : { '': idMap };
115
+ for (const [projectIdn, projectData] of Object.entries(projects)) {
116
+ if (verbose && projectIdn)
117
+ console.log(`📁 Scanning project: ${projectIdn}`);
118
+ for (const [agentIdn, agentObj] of Object.entries(projectData.agents)) {
119
+ if (verbose)
120
+ console.log(` 📁 Scanning agent: ${agentIdn}`);
121
+ for (const [flowIdn, flowObj] of Object.entries(agentObj.flows)) {
122
+ if (verbose)
123
+ console.log(` 📁 Scanning flow: ${flowIdn}`);
124
+ for (const [skillIdn, skillMeta] of Object.entries(flowObj.skills)) {
125
+ const p = projectIdn ?
126
+ skillPath(projectIdn, agentIdn, flowIdn, skillIdn, skillMeta.runner_type) :
127
+ skillPath('', agentIdn, flowIdn, skillIdn, skillMeta.runner_type);
128
+ scanned++;
129
+ if (verbose)
130
+ console.log(` 📄 Checking: ${p}`);
131
+ const content = await readIfExists(p);
132
+ if (content === null) {
133
+ if (verbose)
134
+ console.log(` ⚠️ File not found: ${p}`);
135
+ continue;
136
+ }
137
+ const h = sha256(content);
138
+ const oldHash = oldHashes[p];
139
+ if (verbose) {
140
+ console.log(` 🔍 Hash comparison:`);
141
+ console.log(` Old: ${oldHash || 'none'}`);
142
+ console.log(` New: ${h}`);
143
+ }
144
+ if (oldHash !== h) {
145
+ if (verbose)
146
+ console.log(` 🔄 File changed, preparing to push...`);
147
+ // Create complete skill object with updated prompt_script
148
+ const skillObject = {
149
+ id: skillMeta.id,
150
+ title: skillMeta.title,
151
+ idn: skillMeta.idn,
152
+ prompt_script: content,
153
+ runner_type: skillMeta.runner_type,
154
+ model: skillMeta.model,
155
+ parameters: skillMeta.parameters,
156
+ path: skillMeta.path || undefined
157
+ };
158
+ if (verbose) {
159
+ console.log(` 📤 Pushing skill object:`);
160
+ console.log(` ID: ${skillObject.id}`);
161
+ console.log(` Title: ${skillObject.title}`);
162
+ console.log(` IDN: ${skillObject.idn}`);
163
+ console.log(` Content length: ${content.length} chars`);
164
+ console.log(` Content preview: ${content.substring(0, 100).replace(/\n/g, '\\n')}...`);
165
+ }
166
+ await updateSkill(client, skillObject);
167
+ console.log(`↑ Pushed ${p}`);
168
+ newHashes[p] = h;
169
+ pushed++;
170
+ }
171
+ else if (verbose) {
172
+ console.log(` ✓ No changes`);
173
+ }
174
+ }
175
+ }
176
+ }
177
+ }
178
+ if (verbose)
179
+ console.log(`🔄 Scanned ${scanned} files, found ${pushed} changes`);
180
+ await saveHashes(newHashes);
181
+ console.log(pushed ? `✅ Push complete. ${pushed} file(s) updated.` : '✅ Nothing to push.');
182
+ }
183
+ export async function status(verbose = false) {
184
+ await ensureState();
185
+ if (!(await fs.pathExists(MAP_PATH))) {
186
+ console.log('No map. Run `newo pull` first.');
187
+ return;
188
+ }
189
+ if (verbose)
190
+ console.log('📋 Loading project mapping and hashes...');
191
+ const idMap = await fs.readJson(MAP_PATH);
192
+ const hashes = await loadHashes();
193
+ let dirty = 0;
194
+ // Handle both old single-project format and new multi-project format
195
+ const projects = 'projects' in idMap && idMap.projects ? idMap.projects : { '': idMap };
196
+ for (const [projectIdn, projectData] of Object.entries(projects)) {
197
+ if (verbose && projectIdn)
198
+ console.log(`📁 Checking project: ${projectIdn}`);
199
+ for (const [agentIdn, agentObj] of Object.entries(projectData.agents)) {
200
+ if (verbose)
201
+ console.log(` 📁 Checking agent: ${agentIdn}`);
202
+ for (const [flowIdn, flowObj] of Object.entries(agentObj.flows)) {
203
+ if (verbose)
204
+ console.log(` 📁 Checking flow: ${flowIdn}`);
205
+ for (const [skillIdn, skillMeta] of Object.entries(flowObj.skills)) {
206
+ const p = projectIdn ?
207
+ skillPath(projectIdn, agentIdn, flowIdn, skillIdn, skillMeta.runner_type) :
208
+ skillPath('', agentIdn, flowIdn, skillIdn, skillMeta.runner_type);
209
+ const exists = await fs.pathExists(p);
210
+ if (!exists) {
211
+ console.log(`D ${p}`);
212
+ dirty++;
213
+ if (verbose)
214
+ console.log(` ❌ Deleted: ${p}`);
215
+ continue;
216
+ }
217
+ const content = await fs.readFile(p, 'utf8');
218
+ const h = sha256(content);
219
+ const oldHash = hashes[p];
220
+ if (verbose) {
221
+ console.log(` 📄 ${p}`);
222
+ console.log(` Old hash: ${oldHash || 'none'}`);
223
+ console.log(` New hash: ${h}`);
224
+ }
225
+ if (oldHash !== h) {
226
+ console.log(`M ${p}`);
227
+ dirty++;
228
+ if (verbose)
229
+ console.log(` 🔄 Modified: ${p}`);
230
+ }
231
+ else if (verbose) {
232
+ console.log(` ✓ Unchanged: ${p}`);
233
+ }
234
+ }
235
+ }
236
+ }
237
+ }
238
+ console.log(dirty ? `${dirty} changed file(s).` : 'Clean.');
239
+ }
240
+ async function generateFlowsYaml(client, agents, projectIdn, verbose = false) {
241
+ const flowsData = { flows: [] };
242
+ for (const agent of agents) {
243
+ if (verbose)
244
+ console.log(` 📁 Processing agent: ${agent.idn}`);
245
+ const agentFlows = [];
246
+ for (const flow of agent.flows ?? []) {
247
+ if (verbose)
248
+ console.log(` 📄 Processing flow: ${flow.idn}`);
249
+ // Get skills for this flow
250
+ const skills = await listFlowSkills(client, flow.id);
251
+ const skillsData = skills.map(skill => ({
252
+ idn: skill.idn,
253
+ title: skill.title || "",
254
+ prompt_script: `flows/${flow.idn}/${skill.idn}.${skill.runner_type === 'nsl' ? 'jinja' : 'nsl'}`,
255
+ runner_type: `!enum "RunnerType.${skill.runner_type}"`,
256
+ model: {
257
+ model_idn: skill.model.model_idn,
258
+ provider_idn: skill.model.provider_idn
259
+ },
260
+ parameters: skill.parameters.map(param => ({
261
+ name: param.name,
262
+ default_value: param.default_value || " "
263
+ }))
264
+ }));
265
+ // Get events for this flow
266
+ let eventsData = [];
267
+ try {
268
+ const events = await listFlowEvents(client, flow.id);
269
+ eventsData = events.map(event => ({
270
+ title: event.description,
271
+ idn: event.idn,
272
+ skill_selector: `!enum "SkillSelector.${event.skill_selector}"`,
273
+ skill_idn: event.skill_idn || undefined,
274
+ state_idn: event.state_idn || undefined,
275
+ integration_idn: event.integration_idn || undefined,
276
+ connector_idn: event.connector_idn || undefined,
277
+ interrupt_mode: `!enum "InterruptMode.${event.interrupt_mode}"`
278
+ }));
279
+ if (verbose)
280
+ console.log(` 📋 Found ${events.length} events`);
281
+ }
282
+ catch (error) {
283
+ if (verbose)
284
+ console.log(` ⚠️ No events found for flow ${flow.idn}`);
285
+ }
286
+ // Get state fields for this flow
287
+ let stateFieldsData = [];
288
+ try {
289
+ const states = await listFlowStates(client, flow.id);
290
+ stateFieldsData = states.map(state => ({
291
+ title: state.title,
292
+ idn: state.idn,
293
+ default_value: state.default_value || undefined,
294
+ scope: `!enum "StateFieldScope.${state.scope}"`
295
+ }));
296
+ if (verbose)
297
+ console.log(` 📊 Found ${states.length} state fields`);
298
+ }
299
+ catch (error) {
300
+ if (verbose)
301
+ console.log(` ⚠️ No state fields found for flow ${flow.idn}`);
302
+ }
303
+ agentFlows.push({
304
+ idn: flow.idn,
305
+ title: flow.title,
306
+ description: flow.description || null,
307
+ default_runner_type: `!enum "RunnerType.${flow.default_runner_type}"`,
308
+ default_provider_idn: flow.default_model.provider_idn,
309
+ default_model_idn: flow.default_model.model_idn,
310
+ skills: skillsData,
311
+ events: eventsData,
312
+ state_fields: stateFieldsData
313
+ });
314
+ }
315
+ const agentData = {
316
+ agent_idn: agent.idn,
317
+ agent_description: agent.description || undefined,
318
+ agent_flows: agentFlows
319
+ };
320
+ flowsData.flows.push(agentData);
321
+ }
322
+ // Convert to YAML and write to file with custom enum handling
323
+ let yamlContent = yaml.dump(flowsData, {
324
+ indent: 2,
325
+ lineWidth: -1,
326
+ noRefs: true,
327
+ sortKeys: false,
328
+ quotingType: '"',
329
+ forceQuotes: false
330
+ });
331
+ // Post-process to fix enum formatting
332
+ yamlContent = yamlContent.replace(/"(!enum "[^"]+")"/g, '$1');
333
+ const yamlPath = path.join(projectDir(projectIdn), 'flows.yaml');
334
+ await writeFileAtomic(yamlPath, yamlContent);
335
+ console.log(`✓ Generated flows.yaml for ${projectIdn}`);
336
+ }
337
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Comprehensive type definitions for NEWO CLI
3
+ */
4
+ export interface NewoEnvironment {
5
+ NEWO_BASE_URL?: string;
6
+ NEWO_PROJECT_ID?: string;
7
+ NEWO_API_KEY?: string;
8
+ NEWO_ACCESS_TOKEN?: string;
9
+ NEWO_REFRESH_TOKEN?: string;
10
+ NEWO_REFRESH_URL?: string;
11
+ }
12
+ export interface TokenResponse {
13
+ access_token?: string;
14
+ token?: string;
15
+ accessToken?: string;
16
+ refresh_token?: string;
17
+ refreshToken?: string;
18
+ expires_in?: number;
19
+ expiresIn?: number;
20
+ }
21
+ export interface StoredTokens {
22
+ access_token: string;
23
+ refresh_token: string;
24
+ expires_at: number;
25
+ }
26
+ export interface ProjectMeta {
27
+ id: string;
28
+ idn: string;
29
+ title: string;
30
+ description?: string;
31
+ created_at?: string;
32
+ updated_at?: string;
33
+ }
34
+ export interface Agent {
35
+ id: string;
36
+ idn: string;
37
+ title?: string;
38
+ description?: string;
39
+ flows?: Flow[];
40
+ }
41
+ export interface Flow {
42
+ id: string;
43
+ idn: string;
44
+ title: string;
45
+ description?: string;
46
+ default_runner_type: RunnerType;
47
+ default_model: ModelConfig;
48
+ }
49
+ export interface ModelConfig {
50
+ model_idn: string;
51
+ provider_idn: string;
52
+ }
53
+ export interface SkillParameter {
54
+ name: string;
55
+ default_value?: string;
56
+ }
57
+ export interface Skill {
58
+ id: string;
59
+ idn: string;
60
+ title: string;
61
+ prompt_script?: string;
62
+ runner_type: RunnerType;
63
+ model: ModelConfig;
64
+ parameters: SkillParameter[];
65
+ path?: string | undefined;
66
+ }
67
+ export interface FlowEvent {
68
+ id: string;
69
+ idn: string;
70
+ description: string;
71
+ skill_selector: SkillSelector;
72
+ skill_idn?: string;
73
+ state_idn?: string;
74
+ integration_idn?: string;
75
+ connector_idn?: string;
76
+ interrupt_mode: InterruptMode;
77
+ }
78
+ export interface FlowState {
79
+ id: string;
80
+ idn: string;
81
+ title: string;
82
+ default_value?: string;
83
+ scope: StateFieldScope;
84
+ }
85
+ export type RunnerType = 'guidance' | 'nsl';
86
+ export type SkillSelector = 'first' | 'last' | 'random' | 'all';
87
+ export type InterruptMode = 'allow' | 'deny' | 'queue';
88
+ export type StateFieldScope = 'flow' | 'agent' | 'project' | 'global';
89
+ export interface SkillMetadata {
90
+ id: string;
91
+ title: string;
92
+ idn: string;
93
+ runner_type: RunnerType;
94
+ model: ModelConfig;
95
+ parameters: SkillParameter[];
96
+ path?: string | undefined;
97
+ }
98
+ export interface FlowData {
99
+ id: string;
100
+ skills: Record<string, SkillMetadata>;
101
+ }
102
+ export interface AgentData {
103
+ id: string;
104
+ flows: Record<string, FlowData>;
105
+ }
106
+ export interface ProjectData {
107
+ projectId: string;
108
+ projectIdn: string;
109
+ agents: Record<string, AgentData>;
110
+ }
111
+ export interface ProjectMap {
112
+ projects: Record<string, ProjectData>;
113
+ }
114
+ export interface LegacyProjectMap extends ProjectData {
115
+ projects?: Record<string, ProjectData>;
116
+ }
117
+ export interface HashStore {
118
+ [filePath: string]: string;
119
+ }
120
+ export interface ParsedArticle {
121
+ topic_name: string;
122
+ persona_id: string | null;
123
+ topic_summary: string;
124
+ topic_facts: string[];
125
+ confidence: number;
126
+ source: string;
127
+ labels: string[];
128
+ }
129
+ export interface AkbImportArticle extends Omit<ParsedArticle, 'persona_id'> {
130
+ persona_id: string;
131
+ }
132
+ export interface CliArgs {
133
+ _: string[];
134
+ verbose?: boolean;
135
+ v?: boolean;
136
+ [key: string]: unknown;
137
+ }
138
+ export interface FlowsYamlSkill {
139
+ idn: string;
140
+ title: string;
141
+ prompt_script: string;
142
+ runner_type: string;
143
+ model: ModelConfig;
144
+ parameters: Array<{
145
+ name: string;
146
+ default_value: string;
147
+ }>;
148
+ }
149
+ export interface FlowsYamlEvent {
150
+ title: string;
151
+ idn: string;
152
+ skill_selector: string;
153
+ skill_idn?: string | undefined;
154
+ state_idn?: string | undefined;
155
+ integration_idn?: string | undefined;
156
+ connector_idn?: string | undefined;
157
+ interrupt_mode: string;
158
+ }
159
+ export interface FlowsYamlState {
160
+ title: string;
161
+ idn: string;
162
+ default_value?: string | undefined;
163
+ scope: string;
164
+ }
165
+ export interface FlowsYamlFlow {
166
+ idn: string;
167
+ title: string;
168
+ description: string | null;
169
+ default_runner_type: string;
170
+ default_provider_idn: string;
171
+ default_model_idn: string;
172
+ skills: FlowsYamlSkill[];
173
+ events: FlowsYamlEvent[];
174
+ state_fields: FlowsYamlState[];
175
+ }
176
+ export interface FlowsYamlAgent {
177
+ agent_idn: string;
178
+ agent_description?: string | undefined;
179
+ agent_flows: FlowsYamlFlow[];
180
+ }
181
+ export interface FlowsYamlData {
182
+ flows: FlowsYamlAgent[];
183
+ }
184
+ export interface AxiosClientConfig {
185
+ baseURL?: string;
186
+ headers?: Record<string, string>;
187
+ }
188
+ export interface NewoApiError extends Error {
189
+ response?: {
190
+ status: number;
191
+ data: unknown;
192
+ };
193
+ config?: {
194
+ method?: string;
195
+ url?: string;
196
+ headers?: Record<string, string>;
197
+ };
198
+ }
199
+ export type FileStatus = 'M' | 'D' | 'clean';
200
+ export interface StatusResult {
201
+ filePath: string;
202
+ status: FileStatus;
203
+ oldHash?: string;
204
+ newHash?: string;
205
+ }
206
+ //# sourceMappingURL=types.d.ts.map
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Comprehensive type definitions for NEWO CLI
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "newo",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "NEWO CLI: sync flows/skills between NEWO and local files, multi-project support, import AKB articles",
5
5
  "type": "module",
6
6
  "bin": {
7
- "newo": "src/cli.js"
7
+ "newo": "dist/cli.js"
8
8
  },
9
9
  "files": [
10
- "src/**/*.js",
10
+ "dist/**/*.js",
11
+ "dist/**/*.d.ts",
12
+ "src/**/*.ts",
11
13
  "README.md",
12
14
  "CHANGELOG.md",
13
15
  ".env.example"
@@ -47,16 +49,28 @@
47
49
  "minimist": "^1.2.8"
48
50
  },
49
51
  "devDependencies": {
50
- "mocha": "^10.2.0"
52
+ "@types/fs-extra": "^11.0.4",
53
+ "@types/js-yaml": "^4.0.9",
54
+ "@types/minimist": "^1.2.5",
55
+ "@types/node": "^22.5.4",
56
+ "mocha": "^10.2.0",
57
+ "typescript": "^5.6.2"
51
58
  },
52
59
  "scripts": {
53
- "dev": "node ./src/cli.js",
54
- "pull": "node ./src/cli.js pull",
55
- "push": "node ./src/cli.js push",
56
- "status": "node ./src/cli.js status",
57
- "test": "mocha test/*.test.js --timeout 60000",
58
- "test:api": "mocha test/api.test.js --timeout 30000",
59
- "test:sync": "mocha test/sync.test.js --timeout 60000",
60
- "test:integration": "mocha test/integration.test.js --timeout 120000"
60
+ "build": "tsc",
61
+ "build:watch": "tsc --watch",
62
+ "dev": "npm run build && node ./dist/cli.js",
63
+ "dev:watch": "tsc --watch & nodemon --watch dist dist/cli.js",
64
+ "pull": "npm run build && node ./dist/cli.js pull",
65
+ "push": "npm run build && node ./dist/cli.js push",
66
+ "status": "npm run build && node ./dist/cli.js status",
67
+ "clean": "rm -rf dist",
68
+ "typecheck": "tsc --noEmit",
69
+ "lint": "tsc --noEmit --strict",
70
+ "test": "npm run build && mocha test/*.test.js --timeout 60000",
71
+ "test:api": "npm run build && mocha test/api.test.js --timeout 30000",
72
+ "test:sync": "npm run build && mocha test/sync.test.js --timeout 60000",
73
+ "test:integration": "npm run build && mocha test/integration.test.js --timeout 120000",
74
+ "prepublishOnly": "npm run clean && npm run build"
61
75
  }
62
76
  }