@celilo/cli 0.19.0 → 0.21.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/CELILO_CORE_MODULES.md +2 -2
- package/CELILO_SUBSYSTEMS.md +4 -2
- package/drizzle/0020_dns_registrations_drop_ip.sql +25 -0
- package/drizzle/0021_dns_registration_consumers.sql +63 -0
- package/drizzle/0022_dns_registrations_companion.sql +15 -0
- package/drizzle/0023_public_dns_evidence.sql +19 -0
- package/drizzle/meta/_journal.json +29 -1
- package/package.json +4 -4
- package/schemas/system_config.json +22 -11
- package/src/api/remote-client.test.ts +34 -12
- package/src/api/serve.ts +234 -38
- package/src/api/sessions.test.ts +196 -0
- package/src/api/sessions.ts +278 -0
- package/src/cli/commands/backup-sweep.ts +25 -9
- package/src/cli/commands/dns.ts +8 -4
- package/src/cli/commands/events.ts +64 -5
- package/src/cli/commands/module-update.ts +34 -11
- package/src/cli/commands/system-audit.ts +15 -0
- package/src/cli/commands/system-migrate.test.ts +25 -4
- package/src/cli/commands/system-update.ts +5 -0
- package/src/cli/completion.ts +1 -0
- package/src/cli/index.ts +22 -2
- package/src/cli/tui/audit-state.ts +2 -0
- package/src/db/dns-registrations-migration.test.ts +205 -0
- package/src/db/schema.ts +77 -8
- package/src/hooks/define-hook.test.ts +3 -3
- package/src/hooks/executor.test.ts +58 -0
- package/src/hooks/executor.ts +67 -7
- package/src/hooks/run-named-hook.ts +7 -1
- package/src/hooks/test-fixtures/silent-hook.ts +20 -0
- package/src/module/packaging/build.ts +14 -0
- package/src/services/alerting/builtin-monitors.ts +3 -0
- package/src/services/alerting/builtin-source.ts +23 -0
- package/src/services/audit/index.test.ts +2 -0
- package/src/services/audit/index.ts +3 -0
- package/src/services/audit/public-dns-source.ts +55 -0
- package/src/services/audit/public-dns.test.ts +209 -0
- package/src/services/audit/public-dns.ts +286 -0
- package/src/services/audit/types.ts +1 -0
- package/src/services/backup-metadata.ts +17 -0
- package/src/services/backup-staging.test.ts +98 -0
- package/src/services/backup-staging.ts +73 -1
- package/src/services/backup-sweep.test.ts +15 -0
- package/src/services/backup-sweep.ts +17 -1
- package/src/services/bus-interview-park.test.ts +179 -0
- package/src/services/bus-interview.ts +13 -8
- package/src/services/dns-registrations.test.ts +78 -16
- package/src/services/dns-registrations.ts +107 -19
- package/src/services/fleet-checks.test.ts +47 -1
- package/src/services/fleet-checks.ts +36 -4
- package/src/services/interview-errors.ts +24 -7
- package/src/services/module-subscriptions.test.ts +9 -0
- package/src/services/public-dns-probe.test.ts +81 -0
- package/src/services/public-dns-probe.ts +156 -0
- package/src/services/remote-responder.test.ts +33 -20
- package/src/services/remote-responder.ts +10 -6
- package/src/services/update/orchestrator.test.ts +2 -0
|
@@ -78,12 +78,18 @@ test('answers responder.probe with kind daemon', async () => {
|
|
|
78
78
|
}, 15_000);
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
81
|
+
* A responder that cannot reach a decider must emit NOTHING (celilo#609).
|
|
82
|
+
*
|
|
83
|
+
* PR #607 had it reply `{error}` here, which reads as "fail loudly" but carries
|
|
84
|
+
* `replyFor: <query id>` — it *consumes the query*. The question then no longer
|
|
85
|
+
* exists for anyone else to answer, and the command dies with it. Leaving it
|
|
86
|
+
* unanswered parks the command instead, so a later responder can still decide.
|
|
87
|
+
*
|
|
88
|
+
* Asserted on the value that comes back, not on liveness: the reply emitted
|
|
89
|
+
* afterwards correlates to the original query, which is only true if the
|
|
90
|
+
* responder left it standing.
|
|
85
91
|
*/
|
|
86
|
-
test('an ask that cannot be answered
|
|
92
|
+
test('an ask that cannot be answered emits no reply — the query stays answerable', async () => {
|
|
87
93
|
const responder = startRemoteResponder({
|
|
88
94
|
busDbPath,
|
|
89
95
|
ask: async () => {
|
|
@@ -92,24 +98,31 @@ test('an ask that cannot be answered replies with an error, not the default', as
|
|
|
92
98
|
});
|
|
93
99
|
|
|
94
100
|
const bus = openBus({ dbPath: busDbPath, events: NO_SCHEMAS });
|
|
101
|
+
const type = 'interview.required.module-upgrade:iptables.apply_breaking';
|
|
95
102
|
try {
|
|
96
|
-
const
|
|
97
|
-
'
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
))
|
|
103
|
+
const query = bus.emitRaw(type, {
|
|
104
|
+
scope: 'module-upgrade:iptables',
|
|
105
|
+
key: 'apply_breaking',
|
|
106
|
+
kind: 'confirm',
|
|
107
|
+
message: 'Apply breaking update for iptables?',
|
|
108
|
+
required: true,
|
|
109
|
+
defaultValue: 'false',
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Well past the responder's watch latency: still nobody has answered.
|
|
113
|
+
await new Promise((r) => setTimeout(r, 600));
|
|
114
|
+
expect(bus.recentEvents({ type: `${type}.reply` })).toHaveLength(0);
|
|
108
115
|
|
|
116
|
+
// And the query is still live: a reply emitted now is a genuine answer.
|
|
117
|
+
bus.emitRaw(
|
|
118
|
+
`${type}.reply`,
|
|
119
|
+
{ value: false },
|
|
120
|
+
{ replyFor: query.id, emittedBy: 'later-responder' },
|
|
121
|
+
);
|
|
122
|
+
const replies = bus.recentEvents({ type: `${type}.reply` });
|
|
109
123
|
expect(replies).toHaveLength(1);
|
|
110
|
-
|
|
111
|
-
expect(
|
|
112
|
-
expect(payload.error).toContain('terminal');
|
|
124
|
+
expect((replies[0].payload as { value: unknown }).value).toBe(false);
|
|
125
|
+
expect(replies[0].replyFor).toBe(query.id);
|
|
113
126
|
} finally {
|
|
114
127
|
bus.close();
|
|
115
128
|
responder.close();
|
|
@@ -72,12 +72,16 @@ export function startRemoteResponder(opts: RemoteResponderOptions): RemoteRespon
|
|
|
72
72
|
required: payload.required,
|
|
73
73
|
});
|
|
74
74
|
} catch (err) {
|
|
75
|
-
// The client had no way to answer
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
75
|
+
// The client had no way to answer — so we emit NOTHING. A reply of any
|
|
76
|
+
// shape consumes the query (it carries `replyFor: event.id`), destroying
|
|
77
|
+
// a question nobody has answered yet; the asking command then dies with
|
|
78
|
+
// it and no other responder can ever act. Leaving it unanswered parks the
|
|
79
|
+
// command instead, which is what `busInterview`'s `timeoutMs: 0` is for.
|
|
80
|
+
// The client learns it is parked from the `blocked` wire message.
|
|
81
|
+
process.stderr.write(
|
|
82
|
+
`[remote-responder] parked ${event.type} (#${event.id}): ${
|
|
83
|
+
err instanceof Error ? err.message : String(err)
|
|
84
|
+
}\n`,
|
|
81
85
|
);
|
|
82
86
|
return;
|
|
83
87
|
}
|
|
@@ -2,6 +2,7 @@ import { describe, expect, test } from 'bun:test';
|
|
|
2
2
|
import type { DbClient } from '../../db/client';
|
|
3
3
|
import type { ModuleManifest } from '../../manifest/schema';
|
|
4
4
|
import type { AuditDeps } from '../audit';
|
|
5
|
+
import { unusedPublicDnsProbe } from '../audit/public-dns';
|
|
5
6
|
import { buildModuleGraph } from './dep-graph';
|
|
6
7
|
import {
|
|
7
8
|
type ModuleSnapshot,
|
|
@@ -85,6 +86,7 @@ const cleanAudit: AuditDeps = {
|
|
|
85
86
|
machinesReachable: { results: [] },
|
|
86
87
|
transportReads: { statuses: [], now: new Date(), staleAfterMs: 30 * 60_000 },
|
|
87
88
|
trustedSources: { firewalls: [] },
|
|
89
|
+
publicDns: { records: [], probe: unusedPublicDnsProbe },
|
|
88
90
|
};
|
|
89
91
|
|
|
90
92
|
describe('consumerSkipReason', () => {
|