atris 3.0.1 → 3.2.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/README.md +31 -0
- package/atris/skills/endgame/SKILL.md +19 -1
- package/atris/skills/improve/SKILL.md +65 -62
- package/atris/skills/launch/SKILL.md +62 -0
- package/atris/skills/tidy/SKILL.md +84 -0
- package/bin/atris.js +9 -2
- package/commands/autopilot.js +847 -43
- package/commands/business.js +157 -35
- package/commands/experiments.js +1 -1
- package/commands/release.js +183 -0
- package/commands/research.js +52 -0
- package/commands/sync.js +108 -15
- package/commands/verify.js +3 -3
- package/commands/wiki.js +45 -25
- package/lib/reward-config.js +24 -0
- package/lib/scorecard.js +301 -0
- package/lib/todo.js +12 -2
- package/lib/wiki.js +87 -56
- package/package.json +3 -2
package/commands/sync.js
CHANGED
|
@@ -3,7 +3,17 @@ const path = require('path');
|
|
|
3
3
|
const os = require('os');
|
|
4
4
|
const { ensureWikiScaffold } = require('../lib/wiki');
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const TEMPLATE_ROOT_DIR = path.join(__dirname, '..', 'templates');
|
|
7
|
+
const WORKSPACE_TEMPLATES = {
|
|
8
|
+
business: {
|
|
9
|
+
dir: path.join(TEMPLATE_ROOT_DIR, 'business-starter'),
|
|
10
|
+
label: 'business environment',
|
|
11
|
+
},
|
|
12
|
+
research: {
|
|
13
|
+
dir: path.join(TEMPLATE_ROOT_DIR, 'research-canonical'),
|
|
14
|
+
label: 'research lab environment',
|
|
15
|
+
},
|
|
16
|
+
};
|
|
7
17
|
|
|
8
18
|
/**
|
|
9
19
|
* Walk a directory and return relative file paths.
|
|
@@ -25,13 +35,64 @@ function _walkTemplateDir(dir, base = dir) {
|
|
|
25
35
|
}
|
|
26
36
|
|
|
27
37
|
/**
|
|
28
|
-
* Substitute
|
|
38
|
+
* Substitute workspace metadata in template content.
|
|
29
39
|
*/
|
|
30
40
|
function _substituteParams(content, params) {
|
|
31
41
|
return content
|
|
32
42
|
.replace(/\{\{name\}\}/g, params.name || params.slug || 'this business')
|
|
33
43
|
.replace(/\{\{slug\}\}/g, params.slug || 'business')
|
|
34
|
-
.replace(/\{\{owner_email\}\}/g, params.owner_email || '')
|
|
44
|
+
.replace(/\{\{owner_email\}\}/g, params.owner_email || '')
|
|
45
|
+
.replace(/\{\{business_id\}\}/g, params.business_id || '')
|
|
46
|
+
.replace(/\{\{workspace_id\}\}/g, params.workspace_id || '')
|
|
47
|
+
.replace(/\{\{today\}\}/g, params.today || new Date().toISOString().slice(0, 10))
|
|
48
|
+
.replace(/\{\{workspace_template\}\}/g, params.workspace_template || 'business');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function resolveWorkspaceTemplate(templateName = 'business') {
|
|
52
|
+
const normalized = String(templateName || 'business').toLowerCase();
|
|
53
|
+
if (normalized === 'research-lab' || normalized === 'researchlab' || normalized === 'lab') {
|
|
54
|
+
return { key: 'research', ...WORKSPACE_TEMPLATES.research };
|
|
55
|
+
}
|
|
56
|
+
const template = WORKSPACE_TEMPLATES[normalized];
|
|
57
|
+
if (!template) return null;
|
|
58
|
+
return { key: normalized, ...template };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function _ensureWorkspaceState(targetRoot, params, options = {}) {
|
|
62
|
+
const dryRun = options.dryRun === true;
|
|
63
|
+
const metaDir = path.join(targetRoot, '.atris');
|
|
64
|
+
const stateDir = path.join(metaDir, 'state');
|
|
65
|
+
const created = [];
|
|
66
|
+
|
|
67
|
+
const files = [
|
|
68
|
+
{
|
|
69
|
+
relPath: '_sync.json',
|
|
70
|
+
content: `${JSON.stringify({
|
|
71
|
+
workspace_slug: params.slug || 'business',
|
|
72
|
+
business_id: params.business_id || '',
|
|
73
|
+
workspace_id: params.workspace_id || '',
|
|
74
|
+
workspace_template: params.workspace_template || 'business',
|
|
75
|
+
status: 'initialized-local',
|
|
76
|
+
updated_at: new Date().toISOString(),
|
|
77
|
+
source: 'workspace template bootstrap',
|
|
78
|
+
}, null, 2)}\n`,
|
|
79
|
+
},
|
|
80
|
+
{ relPath: 'events.jsonl', content: '' },
|
|
81
|
+
{ relPath: 'episodes.jsonl', content: '' },
|
|
82
|
+
{ relPath: 'scorecards.jsonl', content: '' },
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
for (const file of files) {
|
|
86
|
+
const fullPath = path.join(stateDir, file.relPath);
|
|
87
|
+
if (fs.existsSync(fullPath)) continue;
|
|
88
|
+
created.push(path.join('.atris', 'state', file.relPath));
|
|
89
|
+
if (!dryRun) {
|
|
90
|
+
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
91
|
+
fs.writeFileSync(fullPath, file.content);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return created;
|
|
35
96
|
}
|
|
36
97
|
|
|
37
98
|
/**
|
|
@@ -41,34 +102,44 @@ function _substituteParams(content, params) {
|
|
|
41
102
|
* Default: NEVER overwrites existing files (preserves customizations).
|
|
42
103
|
* --force: overwrites existing canonical files (bumps to latest).
|
|
43
104
|
*/
|
|
44
|
-
function
|
|
105
|
+
function syncWorkspaceTemplate(targetRoot, bizMeta, options = {}) {
|
|
106
|
+
const template = resolveWorkspaceTemplate(options.templateName || bizMeta.workspace_template || 'business');
|
|
107
|
+
if (!template) {
|
|
108
|
+
console.error(`✗ Unknown workspace template: ${options.templateName || bizMeta.workspace_template}`);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
|
|
45
112
|
const params = {
|
|
46
113
|
slug: bizMeta.slug || 'business',
|
|
47
114
|
name: bizMeta.name || bizMeta.slug || 'this business',
|
|
48
115
|
owner_email: bizMeta.owner_email || '',
|
|
116
|
+
business_id: bizMeta.business_id || '',
|
|
117
|
+
workspace_id: bizMeta.workspace_id || '',
|
|
118
|
+
today: new Date().toISOString().slice(0, 10),
|
|
119
|
+
workspace_template: template.key,
|
|
49
120
|
};
|
|
50
|
-
const force = process.argv.includes('--force');
|
|
51
|
-
const dryRun = process.argv.includes('--dry-run');
|
|
121
|
+
const force = options.force != null ? options.force : process.argv.includes('--force');
|
|
122
|
+
const dryRun = options.dryRun != null ? options.dryRun : process.argv.includes('--dry-run');
|
|
52
123
|
const targetAtrisDir = path.join(targetRoot, 'atris');
|
|
53
124
|
|
|
54
|
-
if (!fs.existsSync(
|
|
55
|
-
console.error(`✗
|
|
125
|
+
if (!fs.existsSync(template.dir)) {
|
|
126
|
+
console.error(`✗ Workspace template directory not found: ${template.dir}`);
|
|
56
127
|
console.error(' Your atris-cli installation may be incomplete.');
|
|
57
128
|
process.exit(1);
|
|
58
129
|
}
|
|
59
130
|
|
|
60
131
|
console.log('');
|
|
61
|
-
console.log(`Updating ${params.name} (${params.slug}) from
|
|
132
|
+
console.log(`Updating ${params.name} (${params.slug}) from ${template.label} templates...`);
|
|
62
133
|
console.log(` Target: ${targetAtrisDir}/`);
|
|
63
|
-
console.log(` Source: ${
|
|
134
|
+
console.log(` Source: ${template.dir}`);
|
|
64
135
|
console.log('');
|
|
65
136
|
|
|
66
|
-
const templateFiles = _walkTemplateDir(
|
|
137
|
+
const templateFiles = _walkTemplateDir(template.dir).sort();
|
|
67
138
|
let added = 0, updated = 0, skipped = 0, preserved = 0;
|
|
68
139
|
const addedList = [], updatedList = [], preservedList = [];
|
|
69
140
|
|
|
70
141
|
for (const relPath of templateFiles) {
|
|
71
|
-
const templatePath = path.join(
|
|
142
|
+
const templatePath = path.join(template.dir, relPath);
|
|
72
143
|
const targetPath = path.join(targetAtrisDir, relPath);
|
|
73
144
|
let templateContent;
|
|
74
145
|
try { templateContent = fs.readFileSync(templatePath, 'utf-8'); } catch { continue; }
|
|
@@ -93,6 +164,8 @@ function syncBusinessCanonical(targetRoot, bizMeta) {
|
|
|
93
164
|
}
|
|
94
165
|
}
|
|
95
166
|
|
|
167
|
+
const stateAddedList = _ensureWorkspaceState(targetRoot, params, { dryRun });
|
|
168
|
+
|
|
96
169
|
console.log(` Added: ${added}`);
|
|
97
170
|
console.log(` Updated: ${updated} ${force ? '' : '(--force to enable)'}`);
|
|
98
171
|
console.log(` Preserved: ${preserved} (existing customizations kept)`);
|
|
@@ -111,10 +184,15 @@ function syncBusinessCanonical(targetRoot, bizMeta) {
|
|
|
111
184
|
if (updatedList.length > 15) console.log(` ... +${updatedList.length - 15} more`);
|
|
112
185
|
console.log('');
|
|
113
186
|
}
|
|
187
|
+
if (stateAddedList.length > 0) {
|
|
188
|
+
console.log(' State files:');
|
|
189
|
+
stateAddedList.forEach(p => console.log(` + ${p}`));
|
|
190
|
+
console.log('');
|
|
191
|
+
}
|
|
114
192
|
|
|
115
193
|
if (dryRun) {
|
|
116
194
|
console.log(' (--dry-run, no changes made)');
|
|
117
|
-
} else if (added === 0 && updated === 0) {
|
|
195
|
+
} else if (added === 0 && updated === 0 && stateAddedList.length === 0) {
|
|
118
196
|
ensureWikiScaffold(targetRoot);
|
|
119
197
|
console.log(' ✓ Already up to date');
|
|
120
198
|
} else {
|
|
@@ -123,13 +201,22 @@ function syncBusinessCanonical(targetRoot, bizMeta) {
|
|
|
123
201
|
}
|
|
124
202
|
}
|
|
125
203
|
|
|
204
|
+
function syncBusinessCanonical(targetRoot, bizMeta, options = {}) {
|
|
205
|
+
return syncWorkspaceTemplate(targetRoot, bizMeta, {
|
|
206
|
+
...options,
|
|
207
|
+
templateName: options.templateName || bizMeta.workspace_template || 'business',
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
126
211
|
function syncAtris() {
|
|
127
212
|
// Business mode detection: if .atris/business.json exists, use canonical templates
|
|
128
213
|
const bizFile = path.join(process.cwd(), '.atris', 'business.json');
|
|
129
214
|
if (fs.existsSync(bizFile)) {
|
|
130
215
|
try {
|
|
131
216
|
const bizMeta = JSON.parse(fs.readFileSync(bizFile, 'utf8'));
|
|
132
|
-
return
|
|
217
|
+
return syncWorkspaceTemplate(process.cwd(), bizMeta, {
|
|
218
|
+
templateName: bizMeta.workspace_template || 'business',
|
|
219
|
+
});
|
|
133
220
|
} catch (e) {
|
|
134
221
|
console.error(`✗ Failed to read .atris/business.json: ${e.message}`);
|
|
135
222
|
process.exit(1);
|
|
@@ -567,4 +654,10 @@ function syncSkills({ silent = false } = {}) {
|
|
|
567
654
|
return updated;
|
|
568
655
|
}
|
|
569
656
|
|
|
570
|
-
module.exports = {
|
|
657
|
+
module.exports = {
|
|
658
|
+
syncAtris,
|
|
659
|
+
syncSkills,
|
|
660
|
+
syncBusinessCanonical,
|
|
661
|
+
syncWorkspaceTemplate,
|
|
662
|
+
resolveWorkspaceTemplate,
|
|
663
|
+
};
|
package/commands/verify.js
CHANGED
|
@@ -444,11 +444,11 @@ function verifyChange(cwd, change) {
|
|
|
444
444
|
};
|
|
445
445
|
}
|
|
446
446
|
|
|
447
|
-
//
|
|
447
|
+
// No specific check possible — refuse to auto-pass
|
|
448
448
|
return {
|
|
449
|
-
pass:
|
|
449
|
+
pass: false,
|
|
450
450
|
description: change.description,
|
|
451
|
-
details: '
|
|
451
|
+
details: 'No verifiable check for this change type. Add an explicit verify command.'
|
|
452
452
|
};
|
|
453
453
|
}
|
|
454
454
|
|
package/commands/wiki.js
CHANGED
|
@@ -5,6 +5,8 @@ const { apiRequestJson } = require('../utils/api');
|
|
|
5
5
|
const { loadBusinesses, saveBusinesses } = require('./business');
|
|
6
6
|
const {
|
|
7
7
|
WIKI_ROOT,
|
|
8
|
+
PRIVATE_WIKI_ROOT,
|
|
9
|
+
getWikiRoot,
|
|
8
10
|
ensureWikiScaffold,
|
|
9
11
|
findLocalWikiDir,
|
|
10
12
|
buildIngestPrompt,
|
|
@@ -31,9 +33,14 @@ function parseCloudArgs(args) {
|
|
|
31
33
|
|
|
32
34
|
function parseModeArgs(args) {
|
|
33
35
|
const cloud = args.includes('--cloud');
|
|
36
|
+
const privateMode = args.includes('--private');
|
|
37
|
+
if (cloud && privateMode) {
|
|
38
|
+
console.error('Use either --cloud or --private, not both.');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
34
41
|
return {
|
|
35
|
-
mode: cloud ? 'cloud' : 'local',
|
|
36
|
-
args: args.filter((arg) => arg !== '--cloud' && arg !== '--local'),
|
|
42
|
+
mode: cloud ? 'cloud' : (privateMode ? 'private' : 'local'),
|
|
43
|
+
args: args.filter((arg) => arg !== '--cloud' && arg !== '--local' && arg !== '--private'),
|
|
37
44
|
};
|
|
38
45
|
}
|
|
39
46
|
|
|
@@ -142,10 +149,10 @@ async function runChat(business, prompt, token) {
|
|
|
142
149
|
}
|
|
143
150
|
}
|
|
144
151
|
|
|
145
|
-
function printLocalPrompt(title, prompt, details = []) {
|
|
152
|
+
function printLocalPrompt(title, prompt, wikiRoot, details = []) {
|
|
146
153
|
console.log('');
|
|
147
154
|
console.log(title);
|
|
148
|
-
console.log(`Target: ${
|
|
155
|
+
console.log(`Target: ${wikiRoot}`);
|
|
149
156
|
details.forEach((detail) => console.log(detail));
|
|
150
157
|
console.log('');
|
|
151
158
|
console.log('Prompt for the current coding agent:');
|
|
@@ -160,9 +167,10 @@ async function wikiIngest(mode, slug, sourceValue) {
|
|
|
160
167
|
process.exit(1);
|
|
161
168
|
}
|
|
162
169
|
|
|
163
|
-
if (mode === 'local') {
|
|
164
|
-
const
|
|
165
|
-
|
|
170
|
+
if (mode === 'local' || mode === 'private') {
|
|
171
|
+
const wikiMode = mode === 'private' ? 'private' : 'public';
|
|
172
|
+
const wikiDir = ensureWikiScaffold(process.cwd(), wikiMode);
|
|
173
|
+
printLocalPrompt(mode === 'private' ? 'Private wiki ingest' : 'Local wiki ingest', buildIngestPrompt(sourceValue, wikiMode), getWikiRoot(wikiMode), [
|
|
166
174
|
`Wiki dir: ${wikiDir}`,
|
|
167
175
|
`Sources: ${sourceValue}`,
|
|
168
176
|
]);
|
|
@@ -182,13 +190,14 @@ async function wikiQuery(mode, slug, question) {
|
|
|
182
190
|
process.exit(1);
|
|
183
191
|
}
|
|
184
192
|
|
|
185
|
-
if (mode
|
|
186
|
-
const
|
|
193
|
+
if (mode !== 'cloud') {
|
|
194
|
+
const wikiMode = mode === 'private' ? 'private' : 'public';
|
|
195
|
+
const wikiDir = findLocalWikiDir(process.cwd(), slug, wikiMode);
|
|
187
196
|
if (!wikiDir) {
|
|
188
|
-
console.error(
|
|
197
|
+
console.error(`No local wiki found at ${getWikiRoot(wikiMode)}. Run: atris wiki ingest${wikiMode === 'private' ? ' --private' : ''} <path>`);
|
|
189
198
|
process.exit(1);
|
|
190
199
|
}
|
|
191
|
-
printLocalPrompt('Local wiki query', buildQueryPrompt(question), [
|
|
200
|
+
printLocalPrompt(mode === 'private' ? 'Private wiki query' : 'Local wiki query', buildQueryPrompt(question, wikiMode), getWikiRoot(wikiMode), [
|
|
192
201
|
`Wiki dir: ${wikiDir}`,
|
|
193
202
|
`Question: ${question}`,
|
|
194
203
|
]);
|
|
@@ -201,13 +210,14 @@ async function wikiQuery(mode, slug, question) {
|
|
|
201
210
|
}
|
|
202
211
|
|
|
203
212
|
async function wikiLint(mode, slug) {
|
|
204
|
-
if (mode
|
|
205
|
-
const
|
|
213
|
+
if (mode !== 'cloud') {
|
|
214
|
+
const wikiMode = mode === 'private' ? 'private' : 'public';
|
|
215
|
+
const wikiDir = findLocalWikiDir(process.cwd(), slug, wikiMode);
|
|
206
216
|
if (!wikiDir) {
|
|
207
|
-
console.error(
|
|
217
|
+
console.error(`No local wiki found at ${getWikiRoot(wikiMode)}. Run: atris wiki ingest${wikiMode === 'private' ? ' --private' : ''} <path>`);
|
|
208
218
|
process.exit(1);
|
|
209
219
|
}
|
|
210
|
-
printLocalPrompt('Local wiki lint', buildLintPrompt(), [`Wiki dir: ${wikiDir}`]);
|
|
220
|
+
printLocalPrompt(mode === 'private' ? 'Private wiki lint' : 'Local wiki lint', buildLintPrompt(wikiMode), getWikiRoot(wikiMode), [`Wiki dir: ${wikiDir}`]);
|
|
211
221
|
return;
|
|
212
222
|
}
|
|
213
223
|
|
|
@@ -217,15 +227,16 @@ async function wikiLint(mode, slug) {
|
|
|
217
227
|
await runChat(business, buildLintPrompt(), creds.token);
|
|
218
228
|
}
|
|
219
229
|
|
|
220
|
-
function wikiSearch(slug, query) {
|
|
230
|
+
function wikiSearch(mode, slug, query) {
|
|
221
231
|
if (!query) {
|
|
222
232
|
console.error('Usage: atris wiki search [business] <term>');
|
|
223
233
|
process.exit(1);
|
|
224
234
|
}
|
|
225
235
|
|
|
226
|
-
const
|
|
236
|
+
const wikiMode = mode === 'private' ? 'private' : 'public';
|
|
237
|
+
const wikiDir = findLocalWikiDir(process.cwd(), slug, wikiMode);
|
|
227
238
|
if (!wikiDir) {
|
|
228
|
-
console.error(`No local wiki found
|
|
239
|
+
console.error(`No local wiki found at ${getWikiRoot(wikiMode)}.`);
|
|
229
240
|
process.exit(1);
|
|
230
241
|
}
|
|
231
242
|
|
|
@@ -250,10 +261,11 @@ function wikiSearch(slug, query) {
|
|
|
250
261
|
console.log('');
|
|
251
262
|
}
|
|
252
263
|
|
|
253
|
-
function wikiLog(slug, limit) {
|
|
254
|
-
const
|
|
264
|
+
function wikiLog(mode, slug, limit) {
|
|
265
|
+
const wikiMode = mode === 'private' ? 'private' : 'public';
|
|
266
|
+
const wikiDir = findLocalWikiDir(process.cwd(), slug, wikiMode);
|
|
255
267
|
if (!wikiDir) {
|
|
256
|
-
console.error(`No local wiki found
|
|
268
|
+
console.error(`No local wiki found at ${getWikiRoot(wikiMode)}.`);
|
|
257
269
|
process.exit(1);
|
|
258
270
|
}
|
|
259
271
|
|
|
@@ -314,14 +326,21 @@ async function wikiCommand(subcommand, ...args) {
|
|
|
314
326
|
break;
|
|
315
327
|
}
|
|
316
328
|
case 'search': {
|
|
317
|
-
|
|
318
|
-
|
|
329
|
+
if (mode === 'private') {
|
|
330
|
+
wikiSearch(mode, null, cleanArgs.join(' '));
|
|
331
|
+
} else {
|
|
332
|
+
const [slug, query] = parseCloudArgs(cleanArgs);
|
|
333
|
+
wikiSearch(mode, slug, query);
|
|
334
|
+
}
|
|
319
335
|
break;
|
|
320
336
|
}
|
|
321
337
|
case 'log': {
|
|
322
338
|
let slug;
|
|
323
339
|
let limit;
|
|
324
|
-
if (
|
|
340
|
+
if (mode === 'private') {
|
|
341
|
+
slug = null;
|
|
342
|
+
limit = parseInt(cleanArgs[0], 10) || 20;
|
|
343
|
+
} else if (cleanArgs.length === 0) {
|
|
325
344
|
slug = autoDetectSlug();
|
|
326
345
|
limit = 20;
|
|
327
346
|
} else if (cleanArgs.length === 1) {
|
|
@@ -336,7 +355,7 @@ async function wikiCommand(subcommand, ...args) {
|
|
|
336
355
|
slug = cleanArgs[0];
|
|
337
356
|
limit = parseInt(cleanArgs[1], 10) || 20;
|
|
338
357
|
}
|
|
339
|
-
wikiLog(slug, limit);
|
|
358
|
+
wikiLog(mode, slug, limit);
|
|
340
359
|
break;
|
|
341
360
|
}
|
|
342
361
|
case 'loop': {
|
|
@@ -361,6 +380,7 @@ async function wikiCommand(subcommand, ...args) {
|
|
|
361
380
|
console.log('Flags:');
|
|
362
381
|
console.log(' --cloud Route ingest/query/lint to the cloud workspace');
|
|
363
382
|
console.log(' --local Be explicit about local mode');
|
|
383
|
+
console.log(` --private Use local private wiki at ${PRIVATE_WIKI_ROOT}/`);
|
|
364
384
|
console.log('');
|
|
365
385
|
console.log('Business is auto-detected from .atris/business.json for cloud mode if omitted.');
|
|
366
386
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frozen reward constants for the RL loop.
|
|
3
|
+
*
|
|
4
|
+
* These live outside mutable repo state so the loop cannot edit its own
|
|
5
|
+
* judge. REWARD_CHECKSUM is the SHA-256 of JSON.stringify(REWARD_CONFIG)
|
|
6
|
+
* + computeTickReward.toString() at ship time — if the config values or
|
|
7
|
+
* function body change, verifyJudgeIntegrity() halts the next tick.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const crypto = require('crypto');
|
|
11
|
+
|
|
12
|
+
const REWARD_CONFIG = Object.freeze({
|
|
13
|
+
REVIEW_CLEAN: 1,
|
|
14
|
+
VERIFY_PASS: 3,
|
|
15
|
+
NPM_TEST_BONUS: 2,
|
|
16
|
+
COMMIT_LANDED: 1,
|
|
17
|
+
HALT_PENALTY: -3,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// SHA-256 of JSON.stringify(REWARD_CONFIG) + computeTickReward.toString() at ship time.
|
|
21
|
+
// Regenerate: node -e "const c=require('./lib/reward-config');const h=require('crypto').createHash('sha256');h.update(JSON.stringify(c.REWARD_CONFIG));h.update(require('./commands/autopilot').computeTickReward.toString());console.log(h.digest('hex'))"
|
|
22
|
+
const REWARD_CHECKSUM = '5a84be0f7f392d6ef05337be0776f864852e94d6391da0b41486298555595a40';
|
|
23
|
+
|
|
24
|
+
module.exports = { REWARD_CONFIG, REWARD_CHECKSUM };
|