atris 3.33.3 → 3.35.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/AGENTS.md +0 -2
- package/FOR_AGENTS.md +5 -3
- package/atris/skills/engines/SKILL.md +16 -7
- package/atris/skills/render-cli/SKILL.md +88 -0
- package/atris.md +19 -0
- package/ax +475 -17
- package/bin/atris.js +206 -44
- package/commands/aeo.js +52 -0
- package/commands/autoland.js +313 -19
- package/commands/business.js +91 -8
- package/commands/codex-goal.js +26 -2
- package/commands/drive.js +187 -0
- package/commands/engine.js +73 -2
- package/commands/feed.js +202 -0
- package/commands/github.js +38 -0
- package/commands/gm.js +262 -3
- package/commands/init.js +13 -1
- package/commands/integrations.js +39 -11
- package/commands/interview.js +143 -0
- package/commands/land.js +114 -13
- package/commands/lesson.js +112 -1
- package/commands/linear.js +38 -0
- package/commands/loops.js +212 -0
- package/commands/member.js +398 -42
- package/commands/mission.js +598 -64
- package/commands/now.js +25 -1
- package/commands/radar.js +259 -14
- package/commands/serve.js +54 -0
- package/commands/status.js +50 -5
- package/commands/stripe.js +38 -0
- package/commands/supabase.js +39 -0
- package/commands/task.js +935 -71
- package/commands/truth.js +29 -3
- package/commands/unknowns.js +627 -0
- package/commands/update.js +44 -0
- package/commands/vercel.js +38 -0
- package/commands/worktree.js +68 -10
- package/commands/write.js +399 -0
- package/lib/auto-accept-certified.js +70 -19
- package/lib/autoland.js +39 -3
- package/lib/fleet.js +256 -13
- package/lib/memory-view.js +14 -5
- package/lib/mission-runtime-loop.js +7 -0
- package/lib/official-cli-integration.js +174 -0
- package/lib/outbound-send-gate.js +165 -0
- package/lib/permission-grants.js +293 -0
- package/lib/review-integrity.js +147 -0
- package/lib/runner-command.js +23 -0
- package/lib/task-db.js +220 -7
- package/lib/task-proof.js +20 -0
- package/lib/task-receipt.js +93 -0
- package/package.json +1 -1
- package/utils/update-check.js +27 -6
- package/atris/learnings.jsonl +0 -1
package/lib/memory-view.js
CHANGED
|
@@ -9,13 +9,18 @@
|
|
|
9
9
|
const fs = require('fs');
|
|
10
10
|
const path = require('path');
|
|
11
11
|
|
|
12
|
-
// "- **[2026-06-18] some-id** — pass — text..." -> { date, id, status, text }
|
|
12
|
+
// "- **[2026-06-18] some-id** — pass — text..." -> { date, id, status, text, resolved }
|
|
13
|
+
// A leading "[resolved]" marker (written when a lesson's detector passes) is
|
|
14
|
+
// stripped from the text and surfaced as `resolved` so the view can retire it.
|
|
13
15
|
function parseLessons(text) {
|
|
14
16
|
const out = [];
|
|
15
17
|
for (const line of String(text || '').split('\n')) {
|
|
16
18
|
const m = line.match(/^-\s*\*\*\[([^\]]+)\]\s*([^*]+?)\*\*\s*[—:-]*\s*(pass|fail)?\s*[—:-]*\s*(.*)$/i);
|
|
17
19
|
if (!m) continue;
|
|
18
|
-
|
|
20
|
+
let body = m[4].trim();
|
|
21
|
+
const resolved = /\[resolved\]/i.test(body);
|
|
22
|
+
body = body.replace(/\[resolved\]\s*/i, '').trim();
|
|
23
|
+
out.push({ date: m[1].trim(), id: m[2].trim(), status: (m[3] || '').toLowerCase(), text: body, resolved });
|
|
19
24
|
}
|
|
20
25
|
return out; // append-only file: oldest first
|
|
21
26
|
}
|
|
@@ -55,15 +60,18 @@ function buildMemorySpec(root = process.cwd(), opts = {}) {
|
|
|
55
60
|
const theme = opts.theme || 'atris';
|
|
56
61
|
const brand = { name: opts.brand || 'Atris', accent: '.' };
|
|
57
62
|
const { lessons, learnings, tasks } = gatherMemory(root);
|
|
63
|
+
const openLessons = lessons.filter((l) => !l.resolved);
|
|
64
|
+
const resolvedCount = lessons.length - openLessons.length;
|
|
58
65
|
const recent = lessons.slice(-8).reverse(); // newest first
|
|
59
66
|
|
|
67
|
+
const retired = resolvedCount ? `, ${plural(resolvedCount, 'lesson')} auto-resolved` : '';
|
|
60
68
|
const slides = [];
|
|
61
69
|
slides.push({
|
|
62
70
|
type: 'title',
|
|
63
71
|
headline: 'What the workspace **learned**',
|
|
64
|
-
sub: `${plural(
|
|
72
|
+
sub: `${plural(openLessons.length, 'open lesson')}${retired} and ${plural(learnings.length, 'note')} compounded into memory${tasks ? `, ${plural(tasks.done, 'task')} done` : ''}.`,
|
|
65
73
|
});
|
|
66
|
-
slides.push({ type: 'bignumber', number: String(
|
|
74
|
+
slides.push({ type: 'bignumber', number: String(openLessons.length), label: `open lessons${resolvedCount ? ` (${resolvedCount} retired)` : ''}` });
|
|
67
75
|
|
|
68
76
|
if (recent.length) {
|
|
69
77
|
slides.push({
|
|
@@ -76,7 +84,8 @@ function buildMemorySpec(root = process.cwd(), opts = {}) {
|
|
|
76
84
|
header: { title: 'Recent updates', meta: `${recent.length} shown` },
|
|
77
85
|
rows: recent.map((l, i) => ({
|
|
78
86
|
title: titleize(l.id), sub: l.date,
|
|
79
|
-
value: l.
|
|
87
|
+
value: l.resolved ? 'resolved' : (l.status || 'note'),
|
|
88
|
+
sev: l.resolved ? 2 : (l.status === 'fail' ? 0 : (l.status === 'pass' ? 2 : 1)),
|
|
80
89
|
active: i === 0,
|
|
81
90
|
})),
|
|
82
91
|
},
|
|
@@ -57,7 +57,10 @@ function missionRunIntentFromMessage(message) {
|
|
|
57
57
|
rest = cadenceFlag.text;
|
|
58
58
|
const noRun = hasBooleanFlag(rest, '--no-run') || hasBooleanFlag(rest, '--no-loop');
|
|
59
59
|
const noComplete = hasBooleanFlag(rest, '--no-complete');
|
|
60
|
+
const forceRoomPreflight = hasBooleanFlag(rest, '--preflight') || hasBooleanFlag(rest, '--room-preflight');
|
|
61
|
+
const skipRoomPreflight = hasBooleanFlag(rest, '--no-preflight') || hasBooleanFlag(rest, '--no-room-preflight') || !forceRoomPreflight;
|
|
60
62
|
rest = stripBooleanFlag(stripBooleanFlag(stripBooleanFlag(rest, '--no-run'), '--no-loop'), '--no-complete');
|
|
63
|
+
rest = stripBooleanFlag(stripBooleanFlag(stripBooleanFlag(stripBooleanFlag(rest, '--preflight'), '--room-preflight'), '--no-preflight'), '--no-room-preflight');
|
|
61
64
|
rest = rest.replace(/(^|\s)--json(?=\s|$)/g, ' ').replace(/\s+/g, ' ').trim();
|
|
62
65
|
|
|
63
66
|
const wantsLongRun = /\b(24\s*h|24\s*hours?|overnight|forever)\b/i.test(text);
|
|
@@ -69,6 +72,8 @@ function missionRunIntentFromMessage(message) {
|
|
|
69
72
|
cadence: cadenceFlag.value || (wantsLongRun ? '15m' : ''),
|
|
70
73
|
noRun,
|
|
71
74
|
noComplete,
|
|
75
|
+
roomPreflight: forceRoomPreflight,
|
|
76
|
+
noRoomPreflight: skipRoomPreflight,
|
|
72
77
|
};
|
|
73
78
|
}
|
|
74
79
|
|
|
@@ -245,6 +250,8 @@ async function runRuntimeMissionLoop(intent, options = {}) {
|
|
|
245
250
|
|
|
246
251
|
const startArgs = ['mission', 'run', intent.objective, '--runner', 'atris2', '--owner', intent.owner || 'mission-lead', '--json'];
|
|
247
252
|
if (intent.cadence) startArgs.push('--cadence', intent.cadence);
|
|
253
|
+
if (intent.roomPreflight) startArgs.push('--room-preflight');
|
|
254
|
+
else if (intent.noRoomPreflight) startArgs.push('--no-room-preflight');
|
|
248
255
|
const start = runCliJsonSync(cliPath, startArgs, { cwd, timeoutMs: 30000 });
|
|
249
256
|
const result = { ok: start.ok, status: start.status, start, noRun: intent.noRun === true, achieved: false };
|
|
250
257
|
if (!start.ok || !start.payload?.mission?.id) {
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
const { spawnSync } = require('node:child_process');
|
|
2
|
+
|
|
3
|
+
function hasHelp(args = []) {
|
|
4
|
+
return args.includes('--help') || args.includes('-h') || args[0] === 'help';
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function firstLine(value) {
|
|
8
|
+
return String(value || '').split(/\r?\n/).map((line) => line.trim()).find(Boolean) || 'unknown';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function formatCommand(parts = []) {
|
|
12
|
+
return parts.join(' ').trim();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function runCaptured(binary, args = []) {
|
|
16
|
+
return spawnSync(binary, args, {
|
|
17
|
+
encoding: 'utf8',
|
|
18
|
+
env: process.env,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function missingBinary(result) {
|
|
23
|
+
return result && result.error && result.error.code === 'ENOENT';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function printMissingBinary(config) {
|
|
27
|
+
console.error(`${config.name} cli not found.`);
|
|
28
|
+
console.error(`install: ${config.installHint}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function ensureBinary(config) {
|
|
32
|
+
const result = runCaptured(config.binary, config.versionArgs || ['--version']);
|
|
33
|
+
if (missingBinary(result)) {
|
|
34
|
+
printMissingBinary(config);
|
|
35
|
+
return { ok: false, result };
|
|
36
|
+
}
|
|
37
|
+
if (result.error) {
|
|
38
|
+
console.error(`${config.name} cli check failed: ${result.error.message}`);
|
|
39
|
+
return { ok: false, result };
|
|
40
|
+
}
|
|
41
|
+
return { ok: true, result };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function runAuthCheck(config) {
|
|
45
|
+
if (!config.authArgs || config.authArgs.length === 0) {
|
|
46
|
+
return { ok: true, skipped: true };
|
|
47
|
+
}
|
|
48
|
+
const result = runCaptured(config.binary, config.authArgs);
|
|
49
|
+
if (missingBinary(result)) {
|
|
50
|
+
printMissingBinary(config);
|
|
51
|
+
return { ok: false, result };
|
|
52
|
+
}
|
|
53
|
+
if (result.error) {
|
|
54
|
+
console.error(`${config.name} auth check failed: ${result.error.message}`);
|
|
55
|
+
return { ok: false, result };
|
|
56
|
+
}
|
|
57
|
+
return { ok: result.status === 0, result };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function printHelp(config) {
|
|
61
|
+
console.log('');
|
|
62
|
+
console.log(`usage: atris ${config.name} <command> [args]`);
|
|
63
|
+
console.log('');
|
|
64
|
+
console.log('commands:');
|
|
65
|
+
console.log(' doctor detect the cli binary and auth state');
|
|
66
|
+
console.log(' auth check auth state');
|
|
67
|
+
for (const command of config.commands) {
|
|
68
|
+
console.log(` ${command.usage.padEnd(13)} ${command.description}`);
|
|
69
|
+
}
|
|
70
|
+
console.log('');
|
|
71
|
+
console.log(`binary: ${config.binary}`);
|
|
72
|
+
console.log(`install: ${config.installHint}`);
|
|
73
|
+
console.log('');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function printDoctor(config) {
|
|
77
|
+
const binary = ensureBinary(config);
|
|
78
|
+
if (!binary.ok) return 1;
|
|
79
|
+
|
|
80
|
+
console.log(`${config.name} integration`);
|
|
81
|
+
console.log(`binary: found ${config.binary}`);
|
|
82
|
+
console.log(`version: ${firstLine(binary.result.stdout || binary.result.stderr).toLowerCase()}`);
|
|
83
|
+
|
|
84
|
+
const auth = runAuthCheck(config);
|
|
85
|
+
if (auth.skipped) {
|
|
86
|
+
console.log('auth: not checked');
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
if (auth.ok) {
|
|
90
|
+
console.log('auth: ok');
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
console.error('auth: failed');
|
|
94
|
+
console.error(`login: ${config.loginHint}`);
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function printAuth(config) {
|
|
99
|
+
const binary = ensureBinary(config);
|
|
100
|
+
if (!binary.ok) return 1;
|
|
101
|
+
const auth = runAuthCheck(config);
|
|
102
|
+
if (auth.skipped || auth.ok) {
|
|
103
|
+
console.log('auth: ok');
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
console.error('auth: failed');
|
|
107
|
+
console.error(`login: ${config.loginHint}`);
|
|
108
|
+
return 1;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function matchCommand(config, args) {
|
|
112
|
+
return config.commands.find((command) => {
|
|
113
|
+
if (args.length < command.match.length) return false;
|
|
114
|
+
return command.match.every((part, index) => args[index] === part);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function runPassthrough(config, command, args) {
|
|
119
|
+
const binary = ensureBinary(config);
|
|
120
|
+
if (!binary.ok) return 1;
|
|
121
|
+
|
|
122
|
+
const rest = args.slice(command.match.length);
|
|
123
|
+
const cliArgs = [...command.forward, ...rest];
|
|
124
|
+
const result = spawnSync(config.binary, cliArgs, {
|
|
125
|
+
stdio: 'inherit',
|
|
126
|
+
env: process.env,
|
|
127
|
+
});
|
|
128
|
+
if (missingBinary(result)) {
|
|
129
|
+
printMissingBinary(config);
|
|
130
|
+
return 1;
|
|
131
|
+
}
|
|
132
|
+
if (result.error) {
|
|
133
|
+
console.error(`${config.name} command failed: ${result.error.message}`);
|
|
134
|
+
return 1;
|
|
135
|
+
}
|
|
136
|
+
return result.status ?? 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function createOfficialCliCommand(config) {
|
|
140
|
+
return function officialCliCommand(args = []) {
|
|
141
|
+
const normalizedArgs = Array.isArray(args) ? args : Array.from(arguments);
|
|
142
|
+
if (normalizedArgs.length === 0 || hasHelp(normalizedArgs)) {
|
|
143
|
+
printHelp(config);
|
|
144
|
+
return 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const subcommand = normalizedArgs[0];
|
|
148
|
+
const doctorAliases = config.doctorAliases || ['doctor', 'status'];
|
|
149
|
+
if (doctorAliases.includes(subcommand)) {
|
|
150
|
+
return printDoctor(config);
|
|
151
|
+
}
|
|
152
|
+
if (subcommand === 'auth') {
|
|
153
|
+
return printAuth(config);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const command = matchCommand(config, normalizedArgs);
|
|
157
|
+
if (!command) {
|
|
158
|
+
console.error(`unknown ${config.name} command: ${formatCommand(normalizedArgs)}`);
|
|
159
|
+
console.error(`run: atris ${config.name} --help`);
|
|
160
|
+
return 1;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return runPassthrough(config, command, normalizedArgs);
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
module.exports = {
|
|
168
|
+
createOfficialCliCommand,
|
|
169
|
+
ensureBinary,
|
|
170
|
+
hasHelp,
|
|
171
|
+
matchCommand,
|
|
172
|
+
printHelp,
|
|
173
|
+
runAuthCheck,
|
|
174
|
+
};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const {
|
|
7
|
+
HTML_TAG_RE,
|
|
8
|
+
RENDERED_SOURCE_FENCE_RE,
|
|
9
|
+
scanOutboundArtifact,
|
|
10
|
+
} = require('../scripts/outbound-artifact-gate');
|
|
11
|
+
|
|
12
|
+
const DEFAULT_GATE = Object.freeze({
|
|
13
|
+
format: 'plain',
|
|
14
|
+
proofFile: null,
|
|
15
|
+
bodyFile: null,
|
|
16
|
+
visual: false,
|
|
17
|
+
allowSlop: false,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
class OutboundArtifactGateError extends Error {
|
|
21
|
+
constructor(check, file, errors = []) {
|
|
22
|
+
super(`outbound artifact gate failed: ${check} in ${file}`);
|
|
23
|
+
this.name = 'OutboundArtifactGateError';
|
|
24
|
+
this.check = check;
|
|
25
|
+
this.file = file;
|
|
26
|
+
this.errors = errors;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function defaultGateOptions() {
|
|
31
|
+
return { ...DEFAULT_GATE };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function readOutboundBodyFile(bodyFile) {
|
|
35
|
+
const file = path.resolve(String(bodyFile || ''));
|
|
36
|
+
try {
|
|
37
|
+
return fs.readFileSync(file, 'utf8');
|
|
38
|
+
} catch {
|
|
39
|
+
throw new OutboundArtifactGateError('body-file-read-error', file);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function writeInlineBodyFile(body) {
|
|
44
|
+
const file = path.join(os.tmpdir(), `atris-outbound-gate-${process.pid}-${Date.now()}.txt`);
|
|
45
|
+
fs.writeFileSync(file, String(body || ''), { encoding: 'utf8', flag: 'wx' });
|
|
46
|
+
return file;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizeProofFile(proofFile) {
|
|
50
|
+
return proofFile ? path.resolve(String(proofFile)) : null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function extractOutboundGateOptions(args = []) {
|
|
54
|
+
const rest = [];
|
|
55
|
+
const gate = defaultGateOptions();
|
|
56
|
+
|
|
57
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
58
|
+
const arg = String(args[i] || '');
|
|
59
|
+
const eq = arg.indexOf('=');
|
|
60
|
+
const key = eq >= 0 ? arg.slice(0, eq) : arg;
|
|
61
|
+
const inlineValue = eq >= 0 ? arg.slice(eq + 1) : null;
|
|
62
|
+
|
|
63
|
+
if (key === '--visual') {
|
|
64
|
+
gate.visual = true;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (key === '--allow-slop') {
|
|
68
|
+
gate.allowSlop = true;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (key === '--format') {
|
|
72
|
+
gate.format = String(inlineValue !== null ? inlineValue : args[i + 1] || 'plain').toLowerCase();
|
|
73
|
+
if (inlineValue === null) i += 1;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (key === '--proof-file' || key === '--render-proof') {
|
|
77
|
+
gate.proofFile = normalizeProofFile(inlineValue !== null ? inlineValue : args[i + 1]);
|
|
78
|
+
if (inlineValue === null) i += 1;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (key === '--body-file') {
|
|
82
|
+
gate.bodyFile = path.resolve(String(inlineValue !== null ? inlineValue : args[i + 1] || ''));
|
|
83
|
+
if (inlineValue === null) i += 1;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
rest.push(arg);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return { args: rest, gate };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function parseOutboundSendArgs(args = []) {
|
|
94
|
+
const parsed = extractOutboundGateOptions(args);
|
|
95
|
+
return {
|
|
96
|
+
text: parsed.gate.bodyFile ? '' : parsed.args.join(' ').trim(),
|
|
97
|
+
gate: parsed.gate,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function shouldRunOutboundGate(channel, body, gate = {}) {
|
|
102
|
+
const format = String(gate.format || 'plain').toLowerCase();
|
|
103
|
+
if (format !== 'plain') return true;
|
|
104
|
+
if (gate.visual || gate.proofFile || gate.bodyFile) return true;
|
|
105
|
+
return HTML_TAG_RE.test(body) || RENDERED_SOURCE_FENCE_RE.test(body);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function assertOutboundSendAllowed(channel, body, gate = {}) {
|
|
109
|
+
const options = { ...defaultGateOptions(), ...(gate || {}) };
|
|
110
|
+
options.format = String(options.format || 'plain').toLowerCase();
|
|
111
|
+
options.channel = String(channel || 'other').toLowerCase();
|
|
112
|
+
options.proofFile = normalizeProofFile(options.proofFile);
|
|
113
|
+
if (options.bodyFile) options.bodyFile = path.resolve(String(options.bodyFile));
|
|
114
|
+
|
|
115
|
+
const inspectedBody = options.bodyFile ? readOutboundBodyFile(options.bodyFile) : String(body || '');
|
|
116
|
+
if (!shouldRunOutboundGate(options.channel, inspectedBody, options)) {
|
|
117
|
+
return { ok: true, inspected: false, body: inspectedBody, file: null };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const inspectedFile = options.bodyFile || writeInlineBodyFile(inspectedBody);
|
|
121
|
+
const errors = scanOutboundArtifact({
|
|
122
|
+
channel: options.channel,
|
|
123
|
+
format: options.format,
|
|
124
|
+
bodyFile: inspectedFile,
|
|
125
|
+
proofFile: options.proofFile,
|
|
126
|
+
visual: options.visual,
|
|
127
|
+
allowSlop: options.allowSlop,
|
|
128
|
+
}, inspectedBody);
|
|
129
|
+
|
|
130
|
+
if (errors.length) {
|
|
131
|
+
throw new OutboundArtifactGateError(errors[0].id, inspectedFile, errors);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return { ok: true, inspected: true, body: inspectedBody, file: inspectedFile };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function exitOutboundGateFailure(error) {
|
|
138
|
+
console.error(error.message);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function assertOutboundSendAllowedOrExit(channel, body, gate = {}, options = {}) {
|
|
143
|
+
try {
|
|
144
|
+
return assertOutboundSendAllowed(channel, body, gate);
|
|
145
|
+
} catch (error) {
|
|
146
|
+
exitOutboundGateFailure(error, options);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function prepareOutboundSendTextOrExit(channel, args = [], options = {}) {
|
|
151
|
+
const parsed = parseOutboundSendArgs(args);
|
|
152
|
+
return assertOutboundSendAllowedOrExit(channel, parsed.text, parsed.gate, options);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
module.exports = {
|
|
156
|
+
OutboundArtifactGateError,
|
|
157
|
+
assertOutboundSendAllowed,
|
|
158
|
+
assertOutboundSendAllowedOrExit,
|
|
159
|
+
defaultGateOptions,
|
|
160
|
+
extractOutboundGateOptions,
|
|
161
|
+
parseOutboundSendArgs,
|
|
162
|
+
prepareOutboundSendTextOrExit,
|
|
163
|
+
readOutboundBodyFile,
|
|
164
|
+
shouldRunOutboundGate,
|
|
165
|
+
};
|