Skip to content

Commit 3d95e6f

Browse files
Merge pull request #97 from NerioVillalobos/metadelta-monitor-command
Metadelta monitor command
2 parents 1e3a5ec + ecc417a commit 3d95e6f

6 files changed

Lines changed: 108 additions & 13 deletions

File tree

README.md

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
> **Last update / Última actualización:** 2026-06-11 — `@nervill/metadelta` 0.11.8
1+
> **Last update / Última actualización:** 2026-06-11 — `@nervill/metadelta` 0.11.9
22
33
# Metadelta Salesforce CLI Plugin
44

@@ -50,7 +50,7 @@ Created by **Nerio Villalobos** (<nervill@gmail.com>).
5050
```bash
5151
sf plugins install github:NerioVillalobos/plugin-metadelta.git
5252
```
53-
Confirm installation with `sf plugins`, which should list `@nervill/metadelta 0.11.8`.
53+
Confirm installation with `sf plugins`, which should list `@nervill/metadelta 0.11.9`.
5454

5555
3. (Optional, for local development) Clone this repository and install dependencies:
5656
```bash
@@ -63,7 +63,7 @@ Created by **Nerio Villalobos** (<nervill@gmail.com>).
6363
npm run build
6464
sf plugins link .
6565
```
66-
Confirm installation with `sf plugins`, which should list `@nervill/metadelta 0.11.8 (link)`.
66+
Confirm installation with `sf plugins`, which should list `@nervill/metadelta 0.11.9 (link)`.
6767

6868
### Usage
6969

@@ -380,13 +380,22 @@ Starts a temporary terminal monitor for Salesforce Core and Vlocity metadata dri
380380
sf metadelta monitor run --org DEV
381381
```
382382

383+
Options:
384+
385+
| Flag | Description | Default |
386+
| --- | --- | --- |
387+
| `--org`, `-o` | Alias or username of the target org. | Required |
388+
| `--interval` | Refresh interval in minutes. Values below `1` are normalized to `1`. | `5` |
389+
| `--scope` | Metadata source to monitor: `all`, `salesforce`, or `vlocity`. | `all` |
390+
| `--once` | Run one refresh cycle and exit after cleanup. Useful for validation. | `false` |
391+
383392
The monitor creates `.metadelta-monitor/`, retrieves the current metadata snapshot, initializes a local-only Git repository as the diff engine, and refreshes every five minutes. `NEXT` shows the exact next refresh time instead of repainting a countdown. The first cycle creates the baseline and shows `STATUS: BASELINE CREATED`; later refreshes show added, modified, deleted, or renamed files. Full errors and Vlocity warnings are wrapped in a detail section and automatically pause UI repainting so the text can be selected/copied.
384393

385394
The UI has two navigable sections. `SALESFORCE CORE / VLOCITY` groups changes by metadata type and shows the count, latest change date, and latest modifier for each type. `RECENT CHANGES` lists the session-cumulative component changes with the most recently detected items first. The arrow keys move the `>` selector across both sections; when the selected row moves past the visible terminal area, the list scrolls so the selector remains visible. Press Enter or `d` on an individual change to see its file, metadata query, modifier, detection time, and Git diff summary. Press Enter or `d` on a metadata type to open `TYPE DETAILS`, which lists the changed components for that type in recent-first order.
386395

387396
Press `p` to pause/resume, `r` to refresh, `s` for Salesforce only, `v` for Vlocity only, `a` for all, and `q`, `x`, `ESC`, `CTRL+C`, or `exit` to quit.
388397

389-
For Vlocity-enabled orgs, the default monitor scope runs `packExportAllDefault` without a job file:
398+
For Vlocity-enabled orgs, the default monitor scope runs `packExportAllDefault` with a temporary job file that includes `continueAfterError: true`:
390399

391400
```bash
392401
sf metadelta monitor run --org DEV --scope vlocity
@@ -403,7 +412,7 @@ sf metadelta monitor run --org DEV --scope salesforce
403412
> **Linked ESM note:** When `sf` prints `@nervill/metadelta is a linked ESM module and cannot be auto-transpiled`, always run `npm run build` before testing commands. If your CLI still does not resolve `sf metadelta task record`, use `sf metadelta:task:record` and relink the plugin. Task diagnostics are saved in `.metadelta/metadelta-task-orchestrator.json`. This is mandatory after local code changes; otherwise `sf` may run stale compiled `lib/` output.
404413
> **Task play hardening:** `sf metadelta task play` now includes automatic stabilizers for frontdoor/base URL separation, initial Setup popup recovery, popup rebinds, App Launcher fallbacks, dynamic Permission Set Assignment selectors, and Action Library scroll selection + Finish enablement checks in the temporary `.metadelta.*` test file.
405414
> **Salesforce CLI secrets workaround (v0.11.4):** `sf metadelta task record` and `sf metadelta task play` build Salesforce frontdoor URLs from the alias passed in `--org`. When they need the real `accessToken`, Metadelta now runs the required `sf org display --target-org <alias> --verbose --json` calls with `SF_TEMP_SHOW_SECRETS=true` in the child process environment. This keeps the automation compatible with Salesforce CLI outputs that redact secrets, without asking users to run `sf org auth ...` interactively or set the workaround globally.
406-
> **Monitor Vlocity support (v0.11.8):** `sf metadelta monitor run` can monitor Vlocity-only sessions with `--scope vlocity` and uses `vlocity -sfdx.username <orgAlias> --projectPath <paths.vlocity> -nojob packExportAllDefault`.
415+
> **Monitor Vlocity support (v0.11.9):** `sf metadelta monitor run` can monitor Vlocity-only sessions with `--scope vlocity` and uses `vlocity -sfdx.username <orgAlias> -job <temporary-yaml> --projectPath <paths.vlocity> packExportAllDefault`. The temporary YAML sets `continueAfterError: true`.
407416
> **Task orchestrator diagnostics:** The orchestrator now stores the most relevant Playwright failure excerpt (not only the exit code), making solution matching and future triage more accurate in `.metadelta/metadelta-task-orchestrator.json`.
408417
> **Report a task-play issue:** If playback fails, please open a public GitHub Issue at <https://github.com/NerioVillalobos/plugin-metadelta/issues> and include: (1) command executed, (2) full error text, (3) screenshot captured while running with `--header`, and (4) sanitized `.metadelta.*` snippet around the failing step.
409418

@@ -1010,13 +1019,22 @@ Inicia un monitor temporal de terminal para detectar drift de metadatos Salesfor
10101019
sf metadelta monitor run --org DEV
10111020
```
10121021
1022+
Opciones:
1023+
1024+
| Flag | Descripción | Valor por defecto |
1025+
| --- | --- | --- |
1026+
| `--org`, `-o` | Alias o username del org destino. | Requerido |
1027+
| `--interval` | Intervalo de refresh en minutos. Los valores menores a `1` se normalizan a `1`. | `5` |
1028+
| `--scope` | Fuente de metadata a monitorear: `all`, `salesforce` o `vlocity`. | `all` |
1029+
| `--once` | Ejecuta un solo ciclo de refresh y sale después del cleanup. Útil para validación. | `false` |
1030+
10131031
El monitor crea `.metadelta-monitor/`, recupera el snapshot actual de metadatos, inicializa un repositorio Git local como motor de diff y refresca cada cinco minutos. `NEXT` muestra la hora exacta del próximo refresh en vez de repintar un countdown. El primer ciclo crea la línea base y muestra `STATUS: BASELINE CREATED`; los siguientes refresh muestran archivos agregados, modificados, eliminados o renombrados. Los errores completos y avisos de Vlocity se muestran envueltos en una sección de detalle y pausan automáticamente el repintado de la UI para poder seleccionar/copiar el texto.
10141032
10151033
La UI tiene dos secciones navegables. `SALESFORCE CORE / VLOCITY` agrupa cambios por tipo de metadata y muestra contador, fecha del último cambio y último usuario modificador para cada tipo. `RECENT CHANGES` lista los cambios acumulados de la sesión con los elementos detectados más recientemente primero. Las flechas mueven el selector `>` por ambas secciones; cuando la fila seleccionada supera el área visible de la terminal, la lista se desplaza para mantener el selector en pantalla. Presiona Enter o `d` sobre un cambio individual para ver archivo, query de metadata, modificador, hora de detección y resumen del diff Git. Presiona Enter o `d` sobre un tipo de metadata para abrir `TYPE DETAILS`, que lista los componentes cambiados de ese tipo en orden reciente primero.
10161034
10171035
Presiona `p` para pausar/reanudar, `r` para refrescar, `s` para solo Salesforce, `v` para solo Vlocity, `a` para todo y `q`, `x`, `ESC`, `CTRL+C` o `exit` para salir.
10181036
1019-
Para orgs con Vlocity habilitado, el scope por defecto del monitor ejecuta `packExportAllDefault` sin archivo job:
1037+
Para orgs con Vlocity habilitado, el scope por defecto del monitor ejecuta `packExportAllDefault` con un job YAML temporal que incluye `continueAfterError: true`:
10201038
10211039
```bash
10221040
sf metadelta monitor run --org DEV --scope vlocity
@@ -1033,7 +1051,7 @@ sf metadelta monitor run --org DEV --scope salesforce
10331051
> **Nota para ESM enlazado:** Si `sf` muestra `@nervill/metadelta is a linked ESM module and cannot be auto-transpiled`, ejecuta `npm run build` antes de probar comandos. Si la CLI no resuelve `sf metadelta task record`, usa `sf metadelta:task:record` y vuelve a enlazar el plugin. El diagnóstico de tareas se guarda en `.metadelta/metadelta-task-orchestrator.json`. Esto es obligatorio tras cambios locales de código; de lo contrario `sf` puede ejecutar un `lib/` compilado desactualizado.
10341052
> **Robustez en task play:** `sf metadelta task play` incluye estabilizadores automáticos para separar frontdoor/base URL, recuperar la apertura inicial del popup de Setup, reabrir popups, aplicar fallback en App Launcher, normalizar selectores dinámicos de Permission Set Assignment y resolver selección con scroll + validación de botón Finish en Action Library dentro del archivo temporal `.metadelta.*`.
10351053
> **Workaround de secretos de Salesforce CLI (v0.11.4):** `sf metadelta task record` y `sf metadelta task play` construyen URLs frontdoor usando el alias recibido en `--org`. Cuando necesitan el `accessToken` real, Metadelta ejecuta las llamadas requeridas a `sf org display --target-org <alias> --verbose --json` con `SF_TEMP_SHOW_SECRETS=true` en el entorno del proceso hijo. Esto mantiene la automatización compatible con salidas de Salesforce CLI que ocultan secretos, sin pedir al usuario ejecutar `sf org auth ...` de forma interactiva ni configurar el workaround globalmente.
1036-
> **Soporte monitor Vlocity (v0.11.8):** `sf metadelta monitor run` puede monitorear sesiones solo Vlocity con `--scope vlocity` y usa `vlocity -sfdx.username <orgAlias> --projectPath <paths.vlocity> -nojob packExportAllDefault`.
1054+
> **Soporte monitor Vlocity (v0.11.9):** `sf metadelta monitor run` puede monitorear sesiones solo Vlocity con `--scope vlocity` y usa `vlocity -sfdx.username <orgAlias> -job <temporary-yaml> --projectPath <paths.vlocity> packExportAllDefault`. El YAML temporal define `continueAfterError: true`.
10371055
> **Diagnóstico del orquestador:** El orquestador ahora guarda el fragmento más relevante del fallo de Playwright (no solo el código de salida), mejorando el match de soluciones y el triage futuro dentro de `.metadelta/metadelta-task-orchestrator.json`.
10381056
> **Reportar incidencias de task play:** Si la reproducción falla, abre un Issue público en GitHub: <https://github.com/NerioVillalobos/plugin-metadelta/issues> e incluye: (1) comando ejecutado, (2) texto completo del error, (3) captura ejecutando con `--header`, y (4) fragmento saneado del archivo `.metadelta.*` en el paso donde falla.
10391057

lib/utils/monitor/retriever.js

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,25 @@ export async function exportVlocity(paths, orgAlias, options = {}) {
3232
}
3333
return { skipped: true, reason };
3434
}
35+
const jobPath = writeVlocityMonitorJob(paths);
3536
try {
36-
await runProcess('vlocity', ['-sfdx.username', orgAlias, '--projectPath', paths.vlocity, '-nojob', 'packExportAllDefault'], { cwd: paths.orgRoot });
37+
await runProcess('vlocity', ['-sfdx.username', orgAlias, '-job', jobPath, '--projectPath', paths.vlocity, 'packExportAllDefault'], { cwd: paths.orgRoot });
3738
}
3839
catch (error) {
3940
removeIgnoredMonitorFiles(paths.vlocity);
41+
const hasExportedFiles = hasMonitorFiles(paths.vlocity);
4042
if (isSampleInputJsonError(error.message)) {
4143
return {
4244
skipped: false,
4345
warning: 'Vlocity export tuvo errores en *_SampleInputJson.json; esos archivos fueron ignorados por el monitor.',
4446
};
4547
}
48+
if (hasExportedFiles) {
49+
return {
50+
skipped: false,
51+
warning: `Vlocity export terminó con errores, pero se conservaron los DataPacks exportados parcialmente:\n${error.message}`,
52+
};
53+
}
4654
if (required) {
4755
throw error;
4856
}
@@ -54,6 +62,35 @@ export async function exportVlocity(paths, orgAlias, options = {}) {
5462
removeIgnoredMonitorFiles(paths.vlocity);
5563
return { skipped: false };
5664
}
65+
function writeVlocityMonitorJob(paths) {
66+
fs.mkdirSync(paths.manifest, { recursive: true });
67+
const jobPath = path.join(paths.manifest, 'monitor-vlocity-export.yaml');
68+
const yaml = [
69+
`projectPath: ${yamlScalar(paths.vlocity)}`,
70+
'continueAfterError: true',
71+
'compileOnBuild: false',
72+
'maxDepth: 0',
73+
'autoUpdateSettings: true',
74+
'',
75+
'OverrideSettings:',
76+
' DataPacks:',
77+
' Catalog:',
78+
' Product2:',
79+
' MaxDeploy: 1',
80+
'',
81+
].join('\n');
82+
fs.writeFileSync(jobPath, yaml, 'utf8');
83+
return jobPath;
84+
}
85+
function yamlScalar(value) {
86+
return `'${String(value).replace(/'/g, "''")}'`;
87+
}
88+
function hasMonitorFiles(root) {
89+
if (!fs.existsSync(root)) {
90+
return false;
91+
}
92+
return collectFiles(root).some((filePath) => !isIgnoredMonitorFile(filePath));
93+
}
5794
function removeIgnoredMonitorFiles(root) {
5895
if (!fs.existsSync(root)) {
5996
return;

oclif.manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -915,5 +915,5 @@
915915
]
916916
}
917917
},
918-
"version": "0.11.8"
918+
"version": "0.11.9"
919919
}

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@nervill/metadelta",
3-
"version": "0.11.8",
3+
"version": "0.11.9",
44
"description": "Salesforce CLI plugin to find recent metadata changes in an org",
55
"type": "module",
66
"main": "lib/index.js",

src/utils/monitor/retriever.js

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,28 @@ export async function exportVlocity(paths, orgAlias, options = {}) {
4040
return {skipped: true, reason};
4141
}
4242

43+
const jobPath = writeVlocityMonitorJob(paths);
4344
try {
4445
await runProcess(
4546
'vlocity',
46-
['-sfdx.username', orgAlias, '--projectPath', paths.vlocity, '-nojob', 'packExportAllDefault'],
47+
['-sfdx.username', orgAlias, '-job', jobPath, '--projectPath', paths.vlocity, 'packExportAllDefault'],
4748
{cwd: paths.orgRoot}
4849
);
4950
} catch (error) {
5051
removeIgnoredMonitorFiles(paths.vlocity);
52+
const hasExportedFiles = hasMonitorFiles(paths.vlocity);
5153
if (isSampleInputJsonError(error.message)) {
5254
return {
5355
skipped: false,
5456
warning: 'Vlocity export tuvo errores en *_SampleInputJson.json; esos archivos fueron ignorados por el monitor.',
5557
};
5658
}
59+
if (hasExportedFiles) {
60+
return {
61+
skipped: false,
62+
warning: `Vlocity export terminó con errores, pero se conservaron los DataPacks exportados parcialmente:\n${error.message}`,
63+
};
64+
}
5765
if (required) {
5866
throw error;
5967
}
@@ -66,6 +74,38 @@ export async function exportVlocity(paths, orgAlias, options = {}) {
6674
return {skipped: false};
6775
}
6876

77+
function writeVlocityMonitorJob(paths) {
78+
fs.mkdirSync(paths.manifest, {recursive: true});
79+
const jobPath = path.join(paths.manifest, 'monitor-vlocity-export.yaml');
80+
const yaml = [
81+
`projectPath: ${yamlScalar(paths.vlocity)}`,
82+
'continueAfterError: true',
83+
'compileOnBuild: false',
84+
'maxDepth: 0',
85+
'autoUpdateSettings: true',
86+
'',
87+
'OverrideSettings:',
88+
' DataPacks:',
89+
' Catalog:',
90+
' Product2:',
91+
' MaxDeploy: 1',
92+
'',
93+
].join('\n');
94+
fs.writeFileSync(jobPath, yaml, 'utf8');
95+
return jobPath;
96+
}
97+
98+
function yamlScalar(value) {
99+
return `'${String(value).replace(/'/g, "''")}'`;
100+
}
101+
102+
function hasMonitorFiles(root) {
103+
if (!fs.existsSync(root)) {
104+
return false;
105+
}
106+
return collectFiles(root).some((filePath) => !isIgnoredMonitorFile(filePath));
107+
}
108+
69109
function removeIgnoredMonitorFiles(root) {
70110
if (!fs.existsSync(root)) {
71111
return;

0 commit comments

Comments
 (0)