@waron97/prbot 3.2.1 → 3.4.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 +26 -18
- package/agrippa-pb.md +141 -68
- package/package.json +5 -1
- package/src/agrippa/commands/clone.js +17 -10
- package/src/agrippa/commands/cloneLrp.js +114 -0
- package/src/agrippa/commands/clonePb.js +10 -10
- package/src/agrippa/commands/diff.js +9 -8
- package/src/agrippa/commands/init.js +14 -13
- package/src/agrippa/commands/initPhase.js +51 -42
- package/src/agrippa/commands/pb.js +71 -39
- package/src/agrippa/commands/pull.js +137 -42
- package/src/agrippa/commands/pullLrp.js +54 -0
- package/src/agrippa/commands/pullPb.js +2 -3
- package/src/agrippa/commands/push.js +112 -33
- package/src/agrippa/commands/pushLrp.js +56 -0
- package/src/agrippa/commands/repair.js +4 -3
- package/src/agrippa/index.js +48 -30
- package/src/agrippa/lib/lrpApi.js +153 -0
- package/src/agrippa/lib/pbEdit.js +31 -10
- package/src/agrippa/lib/pbLayout.js +8 -1
- package/src/agrippa/lib/pbModel.js +318 -86
- package/src/agrippa/lib/pbPreview.js +6 -2
- package/src/agrippa/lib/pbProject.js +93 -7
- package/src/agrippa/lib/pbScriptTemplate.js +29 -0
- package/src/commands/autopr.js +21 -22
- package/src/commands/changelog.js +4 -3
- package/src/commands/commit.js +11 -10
- package/src/commands/export.js +2 -1
- package/src/commands/exportPb.js +19 -2
- package/src/commands/init.js +2 -1
- package/src/commands/routine.js +19 -8
- package/src/index.js +137 -112
- package/src/lib/auth.js +19 -1
- package/src/lib/logger.js +12 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import search from '@inquirer/search';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import { getToken } from '../../lib/auth.js';
|
|
4
|
+
import { log, warn } from '../../lib/logger.js';
|
|
5
|
+
import { loadEffectiveEnv, readConfig, writeConfig } from '../lib/config.js';
|
|
6
|
+
import { getLrpXml, listLrps, resolveLrpByName } from '../lib/lrpApi.js';
|
|
7
|
+
import { checksumOfPayload, comparePayload, decompose, recompose } from '../lib/pbProject.js';
|
|
8
|
+
import { projectReader, writeProject } from '../lib/pbWorkspace.js';
|
|
9
|
+
|
|
10
|
+
// Clone a long-running process (LRP). Structurally identical to a PB clone
|
|
11
|
+
// (same decompose/recompose/checksum machinery — LRPs are plain Activiti BPMN
|
|
12
|
+
// too), but selection and identity are name-based: the tabulator `id` changes
|
|
13
|
+
// on every save/version bump (verified live — a re-fetched id differs from a
|
|
14
|
+
// previously captured one for the same process), so it can never be used as
|
|
15
|
+
// a stable workspace key the way PB's guid is.
|
|
16
|
+
async function cloneLrp(opts) {
|
|
17
|
+
const config = readConfig();
|
|
18
|
+
loadEffectiveEnv(config);
|
|
19
|
+
|
|
20
|
+
if (!process.env.IMPORTEXPORT_URL) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
'IMPORTEXPORT_URL is not configured. Run `prbot init` or set it in agrippa.yaml.'
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const token = await getToken();
|
|
27
|
+
|
|
28
|
+
// Selection: by --name (exact) or interactive server-side search (LRPs
|
|
29
|
+
// are searched server-side by name — see listLrps — not fetched-all-then-
|
|
30
|
+
// fuzzy-filtered like PBs).
|
|
31
|
+
let chosen;
|
|
32
|
+
if (opts.name) {
|
|
33
|
+
chosen = await resolveLrpByName(token, opts.name);
|
|
34
|
+
} else {
|
|
35
|
+
log('Fetching long-running process list...');
|
|
36
|
+
const initial = await listLrps(token, null);
|
|
37
|
+
let controller = null;
|
|
38
|
+
chosen = await search({
|
|
39
|
+
message: 'Select a long-running process:',
|
|
40
|
+
source: async (input) => {
|
|
41
|
+
if (!input) return initial.map((p) => ({ name: p.name, value: p }));
|
|
42
|
+
if (controller) controller.abort();
|
|
43
|
+
controller = new AbortController();
|
|
44
|
+
try {
|
|
45
|
+
const list = await listLrps(token, input, controller.signal);
|
|
46
|
+
return list.map((p) => ({ name: p.name, value: p }));
|
|
47
|
+
} catch {
|
|
48
|
+
return initial.map((p) => ({ name: p.name, value: p }));
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Destination directory.
|
|
55
|
+
let dest = opts.path ?? null;
|
|
56
|
+
if (!dest) {
|
|
57
|
+
const { inputPath } = await inquirer.prompt([
|
|
58
|
+
{
|
|
59
|
+
type: 'input',
|
|
60
|
+
name: 'inputPath',
|
|
61
|
+
message: 'Destination directory:',
|
|
62
|
+
default: chosen.name.replace(/^B2WA_/, ''),
|
|
63
|
+
},
|
|
64
|
+
]);
|
|
65
|
+
dest = inputPath;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
log(`Fetching "${chosen.name}"...`);
|
|
69
|
+
const { xml, description } = await getLrpXml(token, chosen.id);
|
|
70
|
+
const payload = { built_page: xml };
|
|
71
|
+
|
|
72
|
+
const { files } = decompose(payload);
|
|
73
|
+
writeProject(dest, files);
|
|
74
|
+
|
|
75
|
+
const scriptCount = Object.keys(files).filter((p) => p.startsWith('scripts/')).length;
|
|
76
|
+
log(`Cloned to ${dest}/ (${scriptCount} script(s)).`);
|
|
77
|
+
|
|
78
|
+
// Prove the clone reconstructs the original XML (0-loss bar A). LRPs have
|
|
79
|
+
// no `pages` at all — recompose always synthesizes `pages: []`, which
|
|
80
|
+
// comparePayload (shared with PB, where it's meaningful) would otherwise
|
|
81
|
+
// flag as noise on every single LRP clone.
|
|
82
|
+
const rebuilt = recompose(projectReader(dest));
|
|
83
|
+
const diffs = comparePayload(payload, rebuilt).filter((d) => !d.startsWith('pages:'));
|
|
84
|
+
if (diffs.length) {
|
|
85
|
+
warn('WARNING: round-trip verification found differences:');
|
|
86
|
+
diffs.forEach((d) => warn(' - ' + d));
|
|
87
|
+
} else {
|
|
88
|
+
log('Round-trip verified: recomposed payload is identical (0 information loss).');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Register in the workspace for later pull/push — keyed by NAME, not id.
|
|
92
|
+
config.workspace = config.workspace || [];
|
|
93
|
+
const existing = config.workspace.findIndex(
|
|
94
|
+
(e) => e.object_type === 'long_running_process' && e.name === chosen.name
|
|
95
|
+
);
|
|
96
|
+
const entry = {
|
|
97
|
+
path: dest,
|
|
98
|
+
object_type: 'long_running_process',
|
|
99
|
+
name: chosen.name,
|
|
100
|
+
tenant_id: chosen.tenantId,
|
|
101
|
+
svg: chosen.bpmnFileSvg,
|
|
102
|
+
description,
|
|
103
|
+
// Baseline for push classification (see pull.js/push.js): checksum of
|
|
104
|
+
// the *recomposed* payload, changes only when local files change.
|
|
105
|
+
checksum_at_pull: checksumOfPayload(rebuilt),
|
|
106
|
+
version: chosen.version,
|
|
107
|
+
status: chosen.status,
|
|
108
|
+
};
|
|
109
|
+
if (existing >= 0) config.workspace[existing] = entry;
|
|
110
|
+
else config.workspace.push(entry);
|
|
111
|
+
writeConfig(config);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export { cloneLrp };
|
|
@@ -2,10 +2,10 @@ import search from '@inquirer/search';
|
|
|
2
2
|
import inquirer from 'inquirer';
|
|
3
3
|
import { getToken } from '../../lib/auth.js';
|
|
4
4
|
import { fuzzyMatch } from '../../lib/fuzzy.js';
|
|
5
|
-
import {
|
|
5
|
+
import { log, warn } from '../../lib/logger.js';
|
|
6
6
|
import { loadEffectiveEnv, readConfig, writeConfig } from '../lib/config.js';
|
|
7
7
|
import { getProcess, listProcesses } from '../lib/pbApi.js';
|
|
8
|
-
import { comparePayload, decompose, recompose
|
|
8
|
+
import { checksumOfPayload, comparePayload, decompose, recompose } from '../lib/pbProject.js';
|
|
9
9
|
import { projectReader, writeProject } from '../lib/pbWorkspace.js';
|
|
10
10
|
|
|
11
11
|
async function clonePb(opts) {
|
|
@@ -16,11 +16,11 @@ async function clonePb(opts) {
|
|
|
16
16
|
throw new Error('PB_URL is not configured. Run `prbot init` or set it in agrippa.yaml.');
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
log('Fetching process list...');
|
|
20
20
|
const token = await getToken();
|
|
21
21
|
const processes = await listProcesses(token);
|
|
22
22
|
if (!processes.length) {
|
|
23
|
-
|
|
23
|
+
log('No process-builder wizards found.');
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -61,7 +61,7 @@ async function clonePb(opts) {
|
|
|
61
61
|
dest = inputPath;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
log(`Fetching "${chosen.process_name}"...`);
|
|
65
65
|
const payload = await getProcess(token, chosen.guid);
|
|
66
66
|
|
|
67
67
|
const { files } = decompose(payload);
|
|
@@ -69,17 +69,17 @@ async function clonePb(opts) {
|
|
|
69
69
|
|
|
70
70
|
const scriptCount = Object.keys(files).filter((p) => p.startsWith('scripts/')).length;
|
|
71
71
|
const pageCount = Object.keys(files).filter((p) => p.startsWith('pages/')).length;
|
|
72
|
-
|
|
72
|
+
log(`Cloned to ${dest}/ (${scriptCount} script(s), ${pageCount} page(s))`);
|
|
73
73
|
|
|
74
74
|
// Prove the clone reconstructs the original payload (0-loss bar A) by
|
|
75
75
|
// reading the files back from disk and recomposing.
|
|
76
76
|
const rebuilt = recompose(projectReader(dest));
|
|
77
77
|
const diffs = comparePayload(payload, rebuilt);
|
|
78
78
|
if (diffs.length) {
|
|
79
|
-
|
|
80
|
-
diffs.forEach((d) =>
|
|
79
|
+
warn('WARNING: round-trip verification found differences:');
|
|
80
|
+
diffs.forEach((d) => warn(' - ' + d));
|
|
81
81
|
} else {
|
|
82
|
-
|
|
82
|
+
log('Round-trip verified: recomposed payload is identical (0 information loss).');
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// Register in the workspace for later pull/push.
|
|
@@ -95,7 +95,7 @@ async function clonePb(opts) {
|
|
|
95
95
|
name: chosen.process_name,
|
|
96
96
|
// Baselines for `push` classification: checksum of the *recomposed* payload
|
|
97
97
|
// (changes only when local files change) + upstream updated_date/status.
|
|
98
|
-
checksum_at_pull:
|
|
98
|
+
checksum_at_pull: checksumOfPayload(rebuilt),
|
|
99
99
|
updated_date: payload.updated_date,
|
|
100
100
|
status: payload.status,
|
|
101
101
|
};
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { tmpdir } from 'os';
|
|
13
13
|
import { join } from 'path';
|
|
14
14
|
import { getToken } from '../../lib/auth.js';
|
|
15
|
+
import { error, log } from '../../lib/logger.js';
|
|
15
16
|
import { computeChecksum } from '../lib/checksum.js';
|
|
16
17
|
import { loadEffectiveEnv, readConfig } from '../lib/config.js';
|
|
17
18
|
import { getProcess } from '../lib/pbApi.js';
|
|
@@ -25,13 +26,13 @@ async function diff(targetArg) {
|
|
|
25
26
|
loadEffectiveEnv(config);
|
|
26
27
|
|
|
27
28
|
if (!config.workspace.length) {
|
|
28
|
-
|
|
29
|
+
log('No tracked resources. Run `agrippa clone` first.');
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
const entries = filterEntries(config.workspace, targetArg);
|
|
33
34
|
if (!entries.length) {
|
|
34
|
-
|
|
35
|
+
log('No tracked files match the given path.');
|
|
35
36
|
return;
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -43,7 +44,7 @@ async function diff(targetArg) {
|
|
|
43
44
|
if (pbEntries.length && !process.env.PB_URL)
|
|
44
45
|
throw new Error('PB_URL is not configured. Run `prbot init` or set it in agrippa.yaml.');
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
log('Fetching remote code...');
|
|
47
48
|
const token = await getToken();
|
|
48
49
|
|
|
49
50
|
let diffCount = 0;
|
|
@@ -65,13 +66,13 @@ async function diff(targetArg) {
|
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
if (diffCount === 0) {
|
|
68
|
-
|
|
69
|
+
log('No differences found — all tracked files match the remote.');
|
|
69
70
|
} else {
|
|
70
71
|
const combined = Buffer.concat(chunks);
|
|
71
72
|
const pager = process.env.PAGER || 'less';
|
|
72
73
|
const pagerArgs = pager === 'less' ? ['-R', '-F'] : [];
|
|
73
74
|
spawnSync(pager, pagerArgs, { input: combined, stdio: ['pipe', 'inherit', 'inherit'] });
|
|
74
|
-
|
|
75
|
+
log(`\n${diffCount} file(s) differ from remote.`);
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
78
|
|
|
@@ -90,7 +91,7 @@ function diffCodeEntries(entries, remoteCodeMap) {
|
|
|
90
91
|
if (computeChecksum(localCode) === computeChecksum(remoteCode)) continue;
|
|
91
92
|
|
|
92
93
|
if (!fileExists(entry.path)) {
|
|
93
|
-
|
|
94
|
+
log(`\n--- ${entry.path} (local file missing)`);
|
|
94
95
|
continue;
|
|
95
96
|
}
|
|
96
97
|
|
|
@@ -105,7 +106,7 @@ function diffCodeEntries(entries, remoteCodeMap) {
|
|
|
105
106
|
);
|
|
106
107
|
// exit code 1 means differences found (normal), 0 means identical, >1 means error
|
|
107
108
|
if (result.status !== null && result.status > 1) {
|
|
108
|
-
|
|
109
|
+
error(`git diff failed for ${entry.path}`);
|
|
109
110
|
}
|
|
110
111
|
const header = Buffer.from(`\n=== ${entry.path} [${entry.name}] ===\n`);
|
|
111
112
|
chunks.push(Buffer.concat([header, result.stdout ?? Buffer.alloc(0)]));
|
|
@@ -156,7 +157,7 @@ async function diffPbEntry(token, entry) {
|
|
|
156
157
|
{ cwd: tmpRoot, stdio: ['ignore', 'pipe', 'pipe'] }
|
|
157
158
|
);
|
|
158
159
|
if (result.status !== null && result.status > 1) {
|
|
159
|
-
|
|
160
|
+
error(`git diff failed for ${entry.path}`);
|
|
160
161
|
}
|
|
161
162
|
const header = Buffer.from(`\n=== ${entry.path} [${entry.name}] (process-builder) ===\n`);
|
|
162
163
|
const chunk = Buffer.concat([header, result.stdout ?? Buffer.alloc(0)]);
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import { join } from 'path';
|
|
10
10
|
import { fileURLToPath } from 'url';
|
|
11
11
|
import inquirer from 'inquirer';
|
|
12
|
+
import { log } from '../../lib/logger.js';
|
|
12
13
|
import { WORKSPACE_FILE } from '../lib/config.js';
|
|
13
14
|
|
|
14
15
|
const TEMPLATE = `# agrippa workspace configuration
|
|
@@ -93,23 +94,23 @@ async function init() {
|
|
|
93
94
|
},
|
|
94
95
|
]);
|
|
95
96
|
if (!overwrite) {
|
|
96
|
-
|
|
97
|
+
log('Skipped workspace file.');
|
|
97
98
|
} else {
|
|
98
99
|
writeFileSync(WORKSPACE_FILE, TEMPLATE, 'utf-8');
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
log(`Created ${WORKSPACE_FILE}`);
|
|
101
|
+
log(`Run 'agrippa clone' to add resources to this workspace.`);
|
|
102
|
+
log(`Add ${WORKSPACE_FILE} to .gitignore if it contains credentials.`);
|
|
102
103
|
}
|
|
103
104
|
} else {
|
|
104
105
|
writeFileSync(WORKSPACE_FILE, TEMPLATE, 'utf-8');
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
log(`Created ${WORKSPACE_FILE}`);
|
|
107
|
+
log(`Run 'agrippa clone' to add resources to this workspace.`);
|
|
108
|
+
log(`Add ${WORKSPACE_FILE} to .gitignore if it contains credentials.`);
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
if (!existsSync('pyproject.toml')) {
|
|
111
112
|
writeFileSync('pyproject.toml', PYPROJECT_TOML, 'utf-8');
|
|
112
|
-
|
|
113
|
+
log('Created pyproject.toml (ruff builtins)');
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
const { importTypings } = await inquirer.prompt([
|
|
@@ -124,7 +125,7 @@ async function init() {
|
|
|
124
125
|
if (importTypings) {
|
|
125
126
|
if (!existsSync('pyrightconfig.json')) {
|
|
126
127
|
writeFileSync('pyrightconfig.json', PYRIGHTCONFIG, 'utf-8');
|
|
127
|
-
|
|
128
|
+
log('Created pyrightconfig.json');
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
const typingsDir = fileURLToPath(new URL('../../../agrippa_typings', import.meta.url));
|
|
@@ -132,7 +133,7 @@ async function init() {
|
|
|
132
133
|
for (const file of TYPING_FILES) {
|
|
133
134
|
copyFileSync(join(typingsDir, file), join('typings', file));
|
|
134
135
|
}
|
|
135
|
-
|
|
136
|
+
log(`Copied ${TYPING_FILES.length} type stubs to typings/`);
|
|
136
137
|
}
|
|
137
138
|
|
|
138
139
|
const { importInstructions } = await inquirer.prompt([
|
|
@@ -149,13 +150,13 @@ async function init() {
|
|
|
149
150
|
const block = pbBlock(readFileSync(guidePath, 'utf-8'));
|
|
150
151
|
if (!existsSync('CLAUDE.md')) {
|
|
151
152
|
writeFileSync('CLAUDE.md', block, 'utf-8');
|
|
152
|
-
|
|
153
|
+
log('Created CLAUDE.md with agrippa-pb guidance');
|
|
153
154
|
} else {
|
|
154
155
|
const current = readFileSync('CLAUDE.md', 'utf-8');
|
|
155
156
|
const begin = current.indexOf(PB_BEGIN);
|
|
156
157
|
if (begin === -1) {
|
|
157
158
|
appendFileSync('CLAUDE.md', `\n${block}`, 'utf-8');
|
|
158
|
-
|
|
159
|
+
log('Appended agrippa-pb guidance to CLAUDE.md');
|
|
159
160
|
} else {
|
|
160
161
|
// Replace the existing managed block in place; leave the rest as-is.
|
|
161
162
|
const endIdx = current.indexOf(PB_END, begin);
|
|
@@ -165,7 +166,7 @@ async function init() {
|
|
|
165
166
|
);
|
|
166
167
|
const after = current.slice(endIdx + PB_END.length).replace(/^\n/, '');
|
|
167
168
|
writeFileSync('CLAUDE.md', current.slice(0, begin) + block + after, 'utf-8');
|
|
168
|
-
|
|
169
|
+
log('Refreshed agrippa-pb guidance in CLAUDE.md');
|
|
169
170
|
}
|
|
170
171
|
}
|
|
171
172
|
}
|
|
@@ -2,6 +2,7 @@ import search from '@inquirer/search';
|
|
|
2
2
|
import inquirer from 'inquirer';
|
|
3
3
|
import { getToken } from '../../lib/auth.js';
|
|
4
4
|
import { fuzzyMatch } from '../../lib/fuzzy.js';
|
|
5
|
+
import { log } from '../../lib/logger.js';
|
|
5
6
|
import {
|
|
6
7
|
createConfigurator,
|
|
7
8
|
deleteConfigurator,
|
|
@@ -75,12 +76,12 @@ async function initPhase() {
|
|
|
75
76
|
if (!ripUrl)
|
|
76
77
|
throw new Error('RIP_URL is not configured. Run `prbot init` or set it in agrippa.yaml.');
|
|
77
78
|
|
|
78
|
-
|
|
79
|
+
log('Fetching workflows...');
|
|
79
80
|
const token = await getToken();
|
|
80
81
|
const workflows = await listWorkflows(token, ripUrl);
|
|
81
82
|
|
|
82
83
|
if (!workflows.length) {
|
|
83
|
-
|
|
84
|
+
log('No workflows found.');
|
|
84
85
|
return;
|
|
85
86
|
}
|
|
86
87
|
|
|
@@ -92,70 +93,78 @@ async function initPhase() {
|
|
|
92
93
|
},
|
|
93
94
|
});
|
|
94
95
|
|
|
95
|
-
|
|
96
|
+
log(`Fetching phases for "${workflow.name}"...`);
|
|
96
97
|
const phases = await getPhasesByWorkflow(token, ripUrl, workflow.id);
|
|
97
98
|
|
|
98
99
|
if (!phases.length) {
|
|
99
|
-
|
|
100
|
+
log('No phases found for this workflow.');
|
|
100
101
|
return;
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
const { selectedPhases } = await inquirer.prompt([
|
|
105
|
+
{
|
|
106
|
+
type: 'checkbox',
|
|
107
|
+
name: 'selectedPhases',
|
|
108
|
+
message: 'Select phases to initialize:',
|
|
109
|
+
choices: phases.map((p) => ({ name: p.name, value: p })),
|
|
110
|
+
loop: false,
|
|
108
111
|
},
|
|
109
|
-
|
|
112
|
+
]);
|
|
110
113
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
results = await getPhaseResults(token, ripUrl, results);
|
|
114
|
+
if (!selectedPhases.length) {
|
|
115
|
+
log('No phases selected. Aborted.');
|
|
116
|
+
return;
|
|
115
117
|
}
|
|
116
118
|
|
|
117
|
-
const code = generateCode(results);
|
|
118
|
-
|
|
119
119
|
const { confirm } = await inquirer.prompt([
|
|
120
120
|
{
|
|
121
121
|
type: 'confirm',
|
|
122
122
|
name: 'confirm',
|
|
123
|
-
message: `Initialize
|
|
123
|
+
message: `Initialize ${selectedPhases.length} phase(s)? This will overwrite existing code.`,
|
|
124
124
|
default: true,
|
|
125
125
|
},
|
|
126
126
|
]);
|
|
127
127
|
|
|
128
128
|
if (!confirm) {
|
|
129
|
-
|
|
129
|
+
log('Aborted.');
|
|
130
130
|
return;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
133
|
+
for (const phase of selectedPhases) {
|
|
134
|
+
// Resolve result objects — API may return IDs or full objects
|
|
135
|
+
let results = phase.allowed_phase_result_ids || [];
|
|
136
|
+
if (results.length > 0 && typeof results[0] === 'number') {
|
|
137
|
+
results = await getPhaseResults(token, ripUrl, results);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const code = generateCode(results);
|
|
141
|
+
|
|
142
|
+
// Delete existing configurator records for this phase
|
|
143
|
+
const existing = await getPhaseConfigurators(token, ripUrl, phase.id);
|
|
144
|
+
for (const cfg of existing) {
|
|
145
|
+
await deleteConfigurator(token, ripUrl, cfg.id);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Update phase code + set_result_automatically
|
|
149
|
+
await initPhaseRemote(token, ripUrl, phase.id, code);
|
|
150
|
+
|
|
151
|
+
// Create new configurator records for each result
|
|
152
|
+
const vars = results.map((_, i) => `RES${i + 1}`);
|
|
153
|
+
for (let i = 0; i < results.length; i++) {
|
|
154
|
+
await createConfigurator(token, ripUrl, {
|
|
155
|
+
result_value: vars[i],
|
|
156
|
+
code_phase_id: phase.id,
|
|
157
|
+
triplet_phase_result_id: results[i].id,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
log(`Initialized phase "${phase.name}".`);
|
|
162
|
+
if (results.length > 0) {
|
|
163
|
+
log(` ${results.length} configurator(s) created: ${vars.join(', ')}`);
|
|
164
|
+
}
|
|
137
165
|
}
|
|
138
166
|
|
|
139
|
-
|
|
140
|
-
await initPhaseRemote(token, ripUrl, phase.id, code);
|
|
141
|
-
|
|
142
|
-
// Create new configurator records for each result
|
|
143
|
-
const vars = results.map((_, i) => `RES${i + 1}`);
|
|
144
|
-
for (let i = 0; i < results.length; i++) {
|
|
145
|
-
await createConfigurator(token, ripUrl, {
|
|
146
|
-
result_value: vars[i],
|
|
147
|
-
code_phase_id: phase.id,
|
|
148
|
-
triplet_phase_result_id: results[i].id,
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
console.log(`Initialized phase "${phase.name}".`);
|
|
153
|
-
if (results.length > 0) {
|
|
154
|
-
console.log(` ${results.length} configurator(s) created: ${vars.join(', ')}`);
|
|
155
|
-
}
|
|
156
|
-
console.log(
|
|
157
|
-
`Run 'agrippa pull' in workspaces that track this workflow to fetch the updated code.`
|
|
158
|
-
);
|
|
167
|
+
log(`Run 'agrippa pull' in workspaces that track this workflow to fetch the updated code.`);
|
|
159
168
|
}
|
|
160
169
|
|
|
161
170
|
export { initPhase };
|