@celilo/core 0.11.1 → 0.13.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celilo/core",
3
- "version": "0.11.1",
3
+ "version": "0.13.0",
4
4
  "description": "Lightweight shared core for Celilo CLI tools — command registry, NDJSON API protocol, and remote SSH client. No Ink/React/drizzle/aws-sdk transitive deps, so an MCP server can reach the transport without installing the full CLI.",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -742,6 +742,33 @@ export const COMMANDS: CommandDef[] = [
742
742
  },
743
743
  ],
744
744
  },
745
+ {
746
+ name: 'jail',
747
+ description:
748
+ "Get or set one module's hook jail policy (auto|off|required). Omit the policy to show the effective value and which source produced it; --clear removes the row so the module follows the system's hooks.jail_policy again",
749
+ args: [
750
+ { name: 'id', description: 'Module ID', completion: 'module_ids' },
751
+ {
752
+ name: 'policy',
753
+ description:
754
+ 'auto (jail when a backend is available), off (never jail), or required (an unavailable jail is a hard failure); omit to show the effective policy',
755
+ },
756
+ ],
757
+ flags: [
758
+ {
759
+ name: 'clear',
760
+ description:
761
+ "Remove the module's recorded policy so it follows the system's hooks.jail_policy again",
762
+ takesValue: false,
763
+ },
764
+ {
765
+ name: 'force',
766
+ description:
767
+ 'Skip the confirmation interview when the new policy is weaker than the system-wide one',
768
+ takesValue: false,
769
+ },
770
+ ],
771
+ },
745
772
  {
746
773
  name: 'secret',
747
774
  description: 'Manage module secrets',
@@ -826,6 +853,50 @@ export const COMMANDS: CommandDef[] = [
826
853
  description: 'Pre-flight check: validate module is ready for deployment',
827
854
  args: [{ name: 'id', description: 'Module ID', completion: 'module_ids' }],
828
855
  },
856
+ {
857
+ name: 'check',
858
+ description: 'Run drift-detection checks against a third-party module directory',
859
+ args: [{ name: 'path', description: 'Module directory (defaults to ".")' }],
860
+ flags: [
861
+ {
862
+ name: 'no-build',
863
+ description: 'Skip `bunx tsc --noEmit` (fast iteration, or no TypeScript surface)',
864
+ takesValue: false,
865
+ },
866
+ {
867
+ name: 'json',
868
+ description: 'Emit the structured Check[] payload instead of formatted text',
869
+ takesValue: false,
870
+ },
871
+ {
872
+ name: 'strict',
873
+ description: 'Treat warnings as failures (any non-OK is a non-zero exit)',
874
+ takesValue: false,
875
+ },
876
+ ],
877
+ },
878
+ {
879
+ name: 'changeset',
880
+ description: 'Author a version changeset for a module (.changeset/<name>.md)',
881
+ args: [{ name: 'module-dir', description: 'Module directory (defaults to ".")' }],
882
+ flags: [
883
+ {
884
+ name: 'bump',
885
+ description: 'Version bump type: major, minor or patch (required)',
886
+ takesValue: true,
887
+ },
888
+ {
889
+ name: 'message',
890
+ description: 'Changeset body text (default: "<bump> change to <module-id>.")',
891
+ takesValue: true,
892
+ },
893
+ {
894
+ name: 'name',
895
+ description: 'Changeset filename stem (default: auto-generated slug)',
896
+ takesValue: true,
897
+ },
898
+ ],
899
+ },
829
900
  {
830
901
  name: 'status',
831
902
  description: 'Show detailed module status',
@@ -1213,6 +1284,7 @@ export const COMMANDS: CommandDef[] = [
1213
1284
  takesValue: true,
1214
1285
  valueHint: 'dmz app secure internal external',
1215
1286
  },
1287
+ { name: 'json', description: 'Machine-readable JSON output', takesValue: false },
1216
1288
  ],
1217
1289
  },
1218
1290
  {
@@ -1300,6 +1372,18 @@ export const COMMANDS: CommandDef[] = [
1300
1372
  { name: 'key', description: 'Config key', completion: 'system_config_keys' },
1301
1373
  { name: 'value', description: 'Config value' },
1302
1374
  ],
1375
+ // `handleSystemConfigSet` has honoured `flags.force` since the
1376
+ // hooks.jail_policy interview gate landed, and its unit test drives
1377
+ // the handler directly, so the flag was tested and unreachable:
1378
+ // the parser rejected `--force` with "does not accept any flags".
1379
+ flags: [
1380
+ {
1381
+ name: 'force',
1382
+ description:
1383
+ 'Skip the confirmation interview for values that change security posture (hooks.jail_policy off)',
1384
+ takesValue: false,
1385
+ },
1386
+ ],
1303
1387
  },
1304
1388
  {
1305
1389
  name: 'get',
@@ -1402,6 +1486,12 @@ export const COMMANDS: CommandDef[] = [
1402
1486
  description:
1403
1487
  'Diagnose system prerequisites, @celilo/* version drift, and (on a management plane) fleet-runtime drift',
1404
1488
  flags: [
1489
+ {
1490
+ name: 'deep',
1491
+ description:
1492
+ 'Also measure the host plane: one SSH per system for module integrity, plus aspect coverage (celilo#1308: the doctor prescribed --deep before the registry declared it)',
1493
+ takesValue: false,
1494
+ },
1405
1495
  {
1406
1496
  name: 'fix',
1407
1497
  description:
@@ -1661,6 +1751,45 @@ export const COMMANDS: CommandDef[] = [
1661
1751
  description:
1662
1752
  'Per-subscriber delivery history (success rate, last delivery, recent failures)',
1663
1753
  },
1754
+ {
1755
+ name: 'install-daemon',
1756
+ description: 'Write a supervisor unit (systemd/launchd) for the build-bus webhook receiver',
1757
+ flags: [
1758
+ {
1759
+ name: 'system',
1760
+ description: 'System-scope unit (/etc/systemd/system or LaunchDaemons)',
1761
+ takesValue: false,
1762
+ },
1763
+ {
1764
+ name: 'secret',
1765
+ description:
1766
+ 'Shared HMAC secret incoming webhooks sign with (or CELILO_BUS_SECRET env)',
1767
+ takesValue: true,
1768
+ },
1769
+ { name: 'port', description: 'TCP port to listen on (default 8123)', takesValue: true },
1770
+ {
1771
+ name: 'dispatch',
1772
+ description:
1773
+ 'Render a combined unit (receiver + hook dispatcher) for boxes with no events daemon',
1774
+ takesValue: false,
1775
+ },
1776
+ {
1777
+ name: 'print',
1778
+ description: 'Render the unit to stdout without writing (Ansible seam)',
1779
+ takesValue: false,
1780
+ },
1781
+ {
1782
+ name: 'celilo-path',
1783
+ description: 'Explicit celilo binary path for ExecStart',
1784
+ takesValue: true,
1785
+ },
1786
+ ],
1787
+ },
1788
+ {
1789
+ name: 'uninstall-daemon',
1790
+ description: 'Remove the build-bus receiver supervisor unit',
1791
+ flags: [{ name: 'system', description: 'System-scope unit', takesValue: false }],
1792
+ },
1664
1793
  ],
1665
1794
  },
1666
1795
  {
package/src/protocol.ts CHANGED
@@ -100,6 +100,14 @@ export type ProgressMessage = z.infer<typeof ProgressMessageSchema>;
100
100
  export const LogMessageSchema = z.object({
101
101
  type: z.literal('log'),
102
102
  message: z.string(),
103
+ /**
104
+ * Which child stream the line came from. The server merges nothing —
105
+ * each stream is pumped separately — but the message schema is the only
106
+ * place the distinction can survive the trip, so it is recorded here
107
+ * (celilo#1362). Optional so an older client parses a newer server
108
+ * unchanged; unmarked lines are stdout.
109
+ */
110
+ stream: z.enum(['stdout', 'stderr']).optional(),
103
111
  });
104
112
  export type LogMessage = z.infer<typeof LogMessageSchema>;
105
113
 
@@ -190,6 +190,11 @@ function applyMessage(
190
190
  // Nest under the active step so the footer isn't corrupted; otherwise print.
191
191
  if (display.hasPending) {
192
192
  display.subEvent(msg.message);
193
+ } else if (msg.stream === 'stderr') {
194
+ // The server names each log line's origin stream; keep stderr on
195
+ // stderr locally so diagnostics never masquerade as command output
196
+ // (celilo#1362). Unmarked lines are stdout from an older server.
197
+ process.stderr.write(`${msg.message}\n`);
193
198
  } else {
194
199
  out.write(`${msg.message}\n`);
195
200
  }