@celilo/cli 2.2.0 → 2.3.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 +1 -0
- package/CELILO_SUBSYSTEMS.md +1 -1
- package/README.md +1 -1
- package/drizzle/0032_module_jail_policies.sql +28 -0
- package/drizzle/0033_build_bus_hook_runs.sql +37 -0
- package/drizzle/meta/_journal.json +15 -1
- package/package.json +3 -3
- package/schemas/system_config.json +5 -0
- package/src/__integration__/container-services-cli.integration.test.ts +1 -1
- package/src/ansible/inventory.test.ts +2 -1
- package/src/api/sessions.test.ts +5 -4
- package/src/api-clients/proxmox.ts +10 -7
- package/src/cli/backup-rename.test.ts +5 -4
- package/src/cli/cli.test.ts +3 -2
- package/src/cli/commands/console-get-chain.test.ts +2 -1
- package/src/cli/commands/events.test.ts +2 -2
- package/src/cli/commands/firewall-interface-list.test.ts +4 -3
- package/src/cli/commands/machine-list.test.ts +57 -0
- package/src/cli/commands/machine-list.ts +35 -4
- package/src/cli/commands/module-config.test.ts +2 -1
- package/src/cli/commands/module-deploy.ts +8 -2
- package/src/cli/commands/module-generate.test.ts +53 -0
- package/src/cli/commands/module-generate.ts +31 -26
- package/src/cli/commands/module-health.ts +1 -0
- package/src/cli/commands/module-import-aspect.test.ts +2 -1
- package/src/cli/commands/module-import-registry.test.ts +3 -2
- package/src/cli/commands/module-jail.test.ts +242 -0
- package/src/cli/commands/module-jail.ts +227 -0
- package/src/cli/commands/module-list-jail.test.ts +136 -0
- package/src/cli/commands/module-list.ts +30 -3
- package/src/cli/commands/module-operations.test.ts +2 -1
- package/src/cli/commands/module-publish.test.ts +11 -18
- package/src/cli/commands/module-update.test.ts +28 -12
- package/src/cli/commands/module-update.ts +4 -1
- package/src/cli/commands/module-upgrade.test.ts +130 -1
- package/src/cli/commands/module-upgrade.ts +77 -3
- package/src/cli/commands/module-verify.test.ts +3 -4
- package/src/cli/commands/module-verify.ts +0 -1
- package/src/cli/commands/notify-config.test.ts +5 -3
- package/src/cli/commands/publish/execute.ts +4 -1
- package/src/cli/commands/publish/index.ts +11 -1
- package/src/cli/commands/publish/module-registry.test.ts +24 -1
- package/src/cli/commands/publish/module-registry.ts +23 -4
- package/src/cli/commands/publish/plan.ts +1 -1
- package/src/cli/commands/publish/types.ts +7 -0
- package/src/cli/commands/registry-owner.test.ts +3 -2
- package/src/cli/commands/registry-token.test.ts +3 -2
- package/src/cli/commands/system-apply-config-equivalence.test.ts +5 -7
- package/src/cli/commands/system-audit.ts +20 -6
- package/src/cli/commands/system-config.test.ts +148 -0
- package/src/cli/commands/system-config.ts +26 -1
- package/src/cli/commands/system-doctor-remediation-gate.test.ts +295 -0
- package/src/cli/commands/system-doctor.test.ts +143 -6
- package/src/cli/commands/system-doctor.ts +142 -25
- package/src/cli/commands/system-init-deprecation.test.ts +7 -4
- package/src/cli/commands/system-migrate.test.ts +2 -1
- package/src/cli/commands/system-update.ts +5 -0
- package/src/cli/completion.ts +8 -2
- package/src/cli/flag-surface-gate.test.ts +279 -0
- package/src/cli/index.ts +70 -3
- package/src/cli/parser.test.ts +37 -1
- package/src/cli/restore-command.test.ts +5 -4
- package/src/cli/restore-migration-failure.test.ts +4 -3
- package/src/cli/tui/audit-state.ts +2 -0
- package/src/config/paths.test.ts +22 -22
- package/src/db/client.test.ts +46 -1
- package/src/db/client.ts +26 -0
- package/src/db/migrate.test.ts +2 -1
- package/src/db/migrate.ts +16 -16
- package/src/db/schema.ts +63 -0
- package/src/hooks/capability-loader-firewall.test.ts +9 -1
- package/src/hooks/capability-loader.test.ts +78 -0
- package/src/hooks/capability-loader.ts +45 -2
- package/src/hooks/executor.test.ts +161 -1
- package/src/hooks/executor.ts +87 -21
- package/src/hooks/hook-jail-unreachability.test.ts +17 -18
- package/src/hooks/hook-store.test.ts +6 -0
- package/src/hooks/hook-trespass.test.ts +26 -9
- package/src/hooks/jail.test.ts +152 -5
- package/src/hooks/jail.ts +93 -4
- package/src/manifest/contracts/v1.ts +13 -0
- package/src/module/packaging/audit.ts +9 -26
- package/src/module/packaging/build-paths.test.ts +127 -0
- package/src/module/packaging/build-paths.ts +175 -0
- package/src/module/packaging/build.test.ts +71 -1
- package/src/module/packaging/build.ts +60 -0
- package/src/module/packaging/extract.ts +1 -5
- package/src/policy/module-script-scan.ts +51 -107
- package/src/policy/no-hand-built-ssh.test.ts +5 -1
- package/src/policy/no-swallowed-refusal.test.ts +14 -15
- package/src/registry/client.test.ts +2 -2
- package/src/secrets/storage.test.ts +1 -1
- package/src/services/alerting/ack.test.ts +2 -1
- package/src/services/alerting/cadence-migration.test.ts +3 -2
- package/src/services/alerting/coverage-source.test.ts +2 -1
- package/src/services/alerting/deferral.test.ts +2 -1
- package/src/services/alerting/delivery-loop.test.ts +2 -1
- package/src/services/alerting/deploy-hooks.test.ts +2 -1
- package/src/services/alerting/inbound-poller.test.ts +2 -1
- package/src/services/alerting/inbound.test.ts +2 -1
- package/src/services/alerting/keys.test.ts +4 -0
- package/src/services/alerting/keys.ts +12 -2
- package/src/services/alerting/notification-responder.test.ts +2 -1
- package/src/services/alerting/run-monitor.test.ts +2 -1
- package/src/services/alerting/store.test.ts +2 -1
- package/src/services/alerting/sweep-runner.test.ts +2 -1
- package/src/services/alerting/tokens.test.ts +2 -1
- package/src/services/aspect-approvals.test.ts +2 -1
- package/src/services/aspect-reconcile.test.ts +4 -3
- package/src/services/aspect-runner.test.ts +2 -1
- package/src/services/audit/health.test.ts +97 -2
- package/src/services/audit/health.ts +64 -2
- package/src/services/audit/index.test.ts +22 -0
- package/src/services/audit/index.ts +8 -2
- package/src/services/audit/jail-exemptions.test.ts +42 -0
- package/src/services/audit/jail-exemptions.ts +44 -0
- package/src/services/audit/module-integrity.test.ts +23 -22
- package/src/services/audit/module-integrity.ts +7 -16
- package/src/services/audit/types.ts +2 -1
- package/src/services/backup-age-agreement.test.ts +2 -1
- package/src/services/backup-create.ts +9 -0
- package/src/services/backup-envelope-roundtrip.test.ts +3 -2
- package/src/services/backup-in-flight-refusal.test.ts +3 -2
- package/src/services/build-bus/hook-dispatch-mgmt.test.ts +48 -0
- package/src/services/build-bus/hook-dispatcher.ts +49 -1
- package/src/services/bus-ensure-flow.test.ts +3 -2
- package/src/services/bus-interview-park.test.ts +4 -3
- package/src/services/bus-interview.test.ts +2 -2
- package/src/services/bus-secret-flow.test.ts +3 -2
- package/src/services/capability-table-rows.test.ts +2 -1
- package/src/services/celilo-events.test.ts +1 -1
- package/src/services/celilo-mgmt-hooks.test.ts +23 -5
- package/src/services/consumer-cleanup.test.ts +3 -2
- package/src/services/container-service.test.ts +3 -2
- package/src/services/control-plane-bootstrap.test.ts +2 -1
- package/src/services/cross-module-read.test.ts +4 -3
- package/src/services/deploy-preflight.ts +7 -0
- package/src/services/deploy-validation.test.ts +3 -2
- package/src/services/deployed-systems.ts +1 -1
- package/src/services/dns-internal-records.test.ts +3 -2
- package/src/services/dns-provider-backfill.test.ts +2 -1
- package/src/services/dns-registrations.test.ts +2 -1
- package/src/services/ensure-interview.test.ts +3 -2
- package/src/services/fleet-checks.test.ts +126 -6
- package/src/services/fleet-checks.ts +176 -3
- package/src/services/fleet-key.test.ts +3 -2
- package/src/services/health-runner.test.ts +87 -2
- package/src/services/health-runner.ts +40 -11
- package/src/services/infrastructure-selector.test.ts +3 -2
- package/src/services/infrastructure-variable-resolver.test.ts +2 -1
- package/src/services/jail-exemptions.test.ts +125 -0
- package/src/services/jail-exemptions.ts +81 -0
- package/src/services/machine-pool.test.ts +3 -2
- package/src/services/module-config.test.ts +2 -1
- package/src/services/module-deploy.dns-repoint.test.ts +2 -1
- package/src/services/module-deploy.ts +48 -24
- package/src/services/module-operations.test.ts +2 -1
- package/src/services/module-subscriptions.test.ts +4 -3
- package/src/services/network-discovery.test.ts +64 -1
- package/src/services/network-discovery.ts +30 -5
- package/src/services/port-forwards.test.ts +2 -1
- package/src/services/programmatic-responder.aspect.test.ts +2 -1
- package/src/services/proxmox-reconcile.test.ts +2 -1
- package/src/services/responder-probe.test.ts +1 -1
- package/src/services/restore-from-file.test.ts +7 -6
- package/src/services/restore-preflight.test.ts +3 -2
- package/src/services/secret-schema-loader.test.ts +2 -1
- package/src/services/ssh-key-manager.test.ts +4 -3
- package/src/services/static-content-converge.test.ts +5 -4
- package/src/services/static-content-converge.ts +33 -10
- package/src/services/system-state-stage.test.ts +4 -3
- package/src/services/trusted-sources.test.ts +3 -2
- package/src/services/update/orchestrator.test.ts +2 -0
- package/src/services/zone-detector.test.ts +2 -1
- package/src/test-utils/bus-responder.ts +6 -4
- package/src/test-utils/db-path.ts +25 -0
- package/src/test-utils/integration.ts +7 -0
- package/src/test-utils/module-fixtures.ts +5 -6
- package/src/module/packaging/generated-plane.test.ts +0 -79
- package/src/module/packaging/generated-plane.ts +0 -134
package/src/cli/index.ts
CHANGED
|
@@ -80,6 +80,7 @@ import { handleModuleDeploy } from './commands/module-deploy';
|
|
|
80
80
|
import { handleModuleGenerate } from './commands/module-generate';
|
|
81
81
|
import { handleModuleHealth } from './commands/module-health';
|
|
82
82
|
import { handleModuleImport } from './commands/module-import';
|
|
83
|
+
import { handleModuleJail } from './commands/module-jail';
|
|
83
84
|
import { handleModuleJournal } from './commands/module-journal';
|
|
84
85
|
import { handleModuleList } from './commands/module-list';
|
|
85
86
|
import { handleModuleLogs } from './commands/module-logs';
|
|
@@ -315,6 +316,7 @@ Subcommands:
|
|
|
315
316
|
resync-subscriptions Rebuild subscribers from deployed modules' manifests (after a restore/migration)
|
|
316
317
|
list-pending [--subscriber] List pending deliveries
|
|
317
318
|
list-failed [--subscriber] List failed/abandoned deliveries with a true total
|
|
319
|
+
list-unanswered List interview questions nobody has answered yet
|
|
318
320
|
drain [--concurrency N] Process pending deliveries once and return
|
|
319
321
|
run [--poll-ms N] Run the long-running dispatcher (foreground)
|
|
320
322
|
emit <type> [<payload>] Emit an event (operator/test path)
|
|
@@ -588,6 +590,32 @@ Subcommands:
|
|
|
588
590
|
--auto-generate-secrets Auto-generate all secrets without prompting
|
|
589
591
|
|
|
590
592
|
list List all installed modules
|
|
593
|
+
Options:
|
|
594
|
+
--json Output the module roster as stable JSON
|
|
595
|
+
|
|
596
|
+
status <id> Show detailed module status
|
|
597
|
+
|
|
598
|
+
where <id> Show which host(s) serve a module (host discovery)
|
|
599
|
+
Options:
|
|
600
|
+
--json Output host discovery as stable JSON
|
|
601
|
+
|
|
602
|
+
logs <id> Show deploy logs for a module
|
|
603
|
+
Options:
|
|
604
|
+
--tail <n> Show last N lines
|
|
605
|
+
--last Show only the most recent deploy run
|
|
606
|
+
|
|
607
|
+
journal <id> Tail a deployed module's runtime daemon journal on its host (no SSH)
|
|
608
|
+
Options:
|
|
609
|
+
--unit <glob> systemd unit or glob (default: <module-id>*)
|
|
610
|
+
--lines <n> Lines to return (default 100)
|
|
611
|
+
--since <window> Time window, e.g. '30 min ago'
|
|
612
|
+
--grep <text> Only lines containing this fixed string
|
|
613
|
+
--json Machine-readable JSON output
|
|
614
|
+
|
|
615
|
+
operations list|clear Show or release in-flight module operations (the backup/restore lock)
|
|
616
|
+
Options:
|
|
617
|
+
--all With clear: also release operations that still look alive
|
|
618
|
+
--abandoned With list: also show abandoned rows (hidden by default)
|
|
591
619
|
|
|
592
620
|
remove <id> Remove a module and all its data
|
|
593
621
|
|
|
@@ -605,8 +633,12 @@ Subcommands:
|
|
|
605
633
|
--dry-run Print the ordered plan and change nothing
|
|
606
634
|
--yes Skip the cascade confirmation
|
|
607
635
|
|
|
608
|
-
verify <id> Verify module integrity (signature + checksums)
|
|
609
|
-
|
|
636
|
+
verify <id> Verify module integrity (signature + checksums) (legacy alias: 'audit')
|
|
637
|
+
Options:
|
|
638
|
+
--deep Deep verification (re-check all files)
|
|
639
|
+
--json Machine-readable JSON output
|
|
640
|
+
|
|
641
|
+
validate <id> Pre-flight check: validate module is ready for deployment
|
|
610
642
|
|
|
611
643
|
config set <id> <key> <value> Set module configuration value
|
|
612
644
|
config get <id> [key] Get module configuration value(s)
|
|
@@ -616,6 +648,11 @@ Subcommands:
|
|
|
616
648
|
secret get <id> <key> Get a decrypted module secret value
|
|
617
649
|
secret list <id> List module secrets
|
|
618
650
|
|
|
651
|
+
jail <id> [policy] Get or set one module's hook jail policy (auto|off|required)
|
|
652
|
+
Options:
|
|
653
|
+
--clear Remove the module's policy; follow the system's hooks.jail_policy again
|
|
654
|
+
--force Skip the confirmation when the new policy is weaker than the system's
|
|
655
|
+
|
|
619
656
|
show-config <id> Show all config including auto-derived values
|
|
620
657
|
show-zone <id> Show module's network zone and config
|
|
621
658
|
|
|
@@ -631,6 +668,7 @@ Subcommands:
|
|
|
631
668
|
--preflight Run pre-flight validation only (no deployment)
|
|
632
669
|
--verbose Keep all sub-events visible (no collapse-on-success)
|
|
633
670
|
--stop-after-interview Exit after config+secrets interview (no infra/hooks)
|
|
671
|
+
--keep Retain the generated project after a successful deploy (it is deleted by design)
|
|
634
672
|
|
|
635
673
|
health [module-id] Run health checks (transitions to VERIFIED on success)
|
|
636
674
|
Options:
|
|
@@ -646,6 +684,11 @@ Subcommands:
|
|
|
646
684
|
Options:
|
|
647
685
|
--registry <url> Use a custom registry (sweep mode only)
|
|
648
686
|
|
|
687
|
+
upgrade <id> Upgrade a module to its latest registry version (update, posture-gated backup, deploy, verify)
|
|
688
|
+
Options:
|
|
689
|
+
--poll Run the CD poll: upgrade every auto_upgrade module with a newer version
|
|
690
|
+
--registry <url> Registry URL (defaults to configured registry)
|
|
691
|
+
|
|
649
692
|
Registry:
|
|
650
693
|
search [query] Search the registry for modules
|
|
651
694
|
Options:
|
|
@@ -661,6 +704,19 @@ Registry:
|
|
|
661
704
|
--allow-dirty Permit publishing from a dirty git tree
|
|
662
705
|
--allow-stale Skip the manifest-vs-src stale-check. Use sparingly.
|
|
663
706
|
|
|
707
|
+
changeset <module-dir> Author a version changeset for a module (.changeset/<name>.md)
|
|
708
|
+
Options:
|
|
709
|
+
--bump <type> Version bump type: major, minor or patch (required)
|
|
710
|
+
--message <text> Changeset body text
|
|
711
|
+
--name <stem> Changeset filename stem (default: auto-generated slug)
|
|
712
|
+
|
|
713
|
+
backup <module-id> Create a backup of a module's data (invokes its on_backup hook)
|
|
714
|
+
Options:
|
|
715
|
+
--force Ignore schedule, back up now
|
|
716
|
+
--now Ignore schedule, back up now (alias for --force)
|
|
717
|
+
--storage <id> Storage destination ID
|
|
718
|
+
--no-interactive Non-interactive mode (for cron)
|
|
719
|
+
|
|
664
720
|
check [path] Check a module for drift against the framework (default: .)
|
|
665
721
|
Options:
|
|
666
722
|
--no-build Skip the TypeScript build check
|
|
@@ -835,6 +891,12 @@ Subcommands:
|
|
|
835
891
|
--storage <id> Use specific storage destination
|
|
836
892
|
--yes Skip confirmation prompt
|
|
837
893
|
|
|
894
|
+
pull Download another box's backup artifact from a storage destination
|
|
895
|
+
Options:
|
|
896
|
+
--storage <name> Storage destination name
|
|
897
|
+
--module <id> Module ID whose backup to pull (default: system state)
|
|
898
|
+
--output <path> Local path to write the artifact
|
|
899
|
+
|
|
838
900
|
Description:
|
|
839
901
|
Backups are encrypted with the master key and uploaded to a configured
|
|
840
902
|
storage destination. System state backups capture the Celilo database.
|
|
@@ -1055,6 +1117,7 @@ Usage:
|
|
|
1055
1117
|
Resources:
|
|
1056
1118
|
vmid Manage VMID reservations and allocations
|
|
1057
1119
|
ip Manage IP address reservations
|
|
1120
|
+
show Show comprehensive IPAM summary
|
|
1058
1121
|
list-allocations List all IPAM allocations (VMIDs and IPs)
|
|
1059
1122
|
|
|
1060
1123
|
VMID Commands:
|
|
@@ -1139,6 +1202,8 @@ Subcommands:
|
|
|
1139
1202
|
update [--module <id>] [--no-backup] [--allow-destructive] [--dry-run] [--json]
|
|
1140
1203
|
Bring the system to the audit-determined READY state
|
|
1141
1204
|
|
|
1205
|
+
migrate Apply pending database migrations (idempotent; safe to re-run)
|
|
1206
|
+
|
|
1142
1207
|
doctor [--fix] Diagnose system prerequisites and @celilo/* version drift
|
|
1143
1208
|
|
|
1144
1209
|
Runbook:
|
|
@@ -1608,6 +1673,8 @@ export async function runCli(argv: string[]): Promise<CommandResult> {
|
|
|
1608
1673
|
return handleModuleVersion(parsed.args, parsed.flags);
|
|
1609
1674
|
case 'changeset':
|
|
1610
1675
|
return handleModuleChangeset(parsed.args, parsed.flags);
|
|
1676
|
+
case 'jail':
|
|
1677
|
+
return handleModuleJail(parsed.args, parsed.flags);
|
|
1611
1678
|
case 'audit':
|
|
1612
1679
|
return moduleAudit(parsed.args, parsed.flags);
|
|
1613
1680
|
case 'verify':
|
|
@@ -2238,7 +2305,7 @@ export async function runCli(argv: string[]): Promise<CommandResult> {
|
|
|
2238
2305
|
}
|
|
2239
2306
|
const configArgs = parsed.args.slice(1);
|
|
2240
2307
|
if (configSubcommand === 'set') {
|
|
2241
|
-
return handleSystemConfigSet(configArgs);
|
|
2308
|
+
return await handleSystemConfigSet(configArgs, parsed.flags);
|
|
2242
2309
|
}
|
|
2243
2310
|
if (configSubcommand === 'get') {
|
|
2244
2311
|
return handleSystemConfigGet(configArgs);
|
package/src/cli/parser.test.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test';
|
|
2
|
-
import {
|
|
2
|
+
import { COMMANDS } from '@celilo/core';
|
|
3
|
+
import {
|
|
4
|
+
getArg,
|
|
5
|
+
getFlag,
|
|
6
|
+
hasFlag,
|
|
7
|
+
parseArguments,
|
|
8
|
+
validateFlags,
|
|
9
|
+
validateRequiredArgs,
|
|
10
|
+
} from './parser';
|
|
3
11
|
|
|
4
12
|
describe('CLI Parser', () => {
|
|
5
13
|
describe('parseArguments', () => {
|
|
@@ -238,3 +246,31 @@ describe('CLI Parser', () => {
|
|
|
238
246
|
});
|
|
239
247
|
});
|
|
240
248
|
});
|
|
249
|
+
|
|
250
|
+
describe('a flag a handler reads is a flag the registry declares', () => {
|
|
251
|
+
// `handleSystemConfigSet` has read `flags.force` since the hooks.jail_policy
|
|
252
|
+
// interview gate landed, and its unit test drove the handler directly with
|
|
253
|
+
// `{ force: true }`. The registry declared no flags for `system config set`,
|
|
254
|
+
// so the parser answered `--force` with "does not accept any flags" and the
|
|
255
|
+
// escape hatch was tested and unreachable from a terminal. The suite could
|
|
256
|
+
// not see it, because nothing in it went through the parser.
|
|
257
|
+
const leaf = (path: readonly string[]) =>
|
|
258
|
+
path.slice(1).reduce<{ name: string; subcommands?: unknown[] } | undefined>(
|
|
259
|
+
(def, name) =>
|
|
260
|
+
(def as { subcommands?: { name: string }[] } | undefined)?.subcommands?.find(
|
|
261
|
+
(s) => s.name === name,
|
|
262
|
+
),
|
|
263
|
+
COMMANDS.find((c) => c.name === path[0]),
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
test('system config set accepts --force through the real registry', () => {
|
|
267
|
+
const def = leaf(['system', 'config', 'set']);
|
|
268
|
+
expect(def).toBeDefined();
|
|
269
|
+
expect(validateFlags({ force: true }, def as never)).toBeNull();
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test('and still refuses a flag nobody declared', () => {
|
|
273
|
+
const def = leaf(['system', 'config', 'set']);
|
|
274
|
+
expect(validateFlags({ frce: true }, def as never)).toContain('Unknown flag');
|
|
275
|
+
});
|
|
276
|
+
});
|
|
@@ -16,6 +16,7 @@ import { join } from 'node:path';
|
|
|
16
16
|
import { closeDb, getDb } from '../db/client';
|
|
17
17
|
import { runMigrations } from '../db/migrate';
|
|
18
18
|
import { modules } from '../db/schema';
|
|
19
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
19
20
|
import { runCli } from './index';
|
|
20
21
|
|
|
21
22
|
describe('celilo restore', () => {
|
|
@@ -32,10 +33,10 @@ describe('celilo restore', () => {
|
|
|
32
33
|
|
|
33
34
|
afterEach(() => {
|
|
34
35
|
closeDb();
|
|
35
|
-
|
|
36
|
-
process.env.CELILO_DATA_DIR
|
|
37
|
-
process.env.CELILO_MASTER_KEY_PATH
|
|
38
|
-
process.env.CELILO_SUPPRESS_DEPRECATION
|
|
36
|
+
resetTestDbPath();
|
|
37
|
+
delete process.env.CELILO_DATA_DIR;
|
|
38
|
+
delete process.env.CELILO_MASTER_KEY_PATH;
|
|
39
|
+
delete process.env.CELILO_SUPPRESS_DEPRECATION;
|
|
39
40
|
try {
|
|
40
41
|
rmSync(dir, { recursive: true, force: true });
|
|
41
42
|
} catch {
|
|
@@ -28,6 +28,7 @@ import { getOrCreateMasterKey } from '../secrets/master-key';
|
|
|
28
28
|
import { encryptFileToFile } from '../services/backup-cipher';
|
|
29
29
|
import { buildManifest } from '../services/backup-manifest';
|
|
30
30
|
import { migrateRestoredDb } from '../services/restore-from-file';
|
|
31
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
31
32
|
import { runCli } from './index';
|
|
32
33
|
|
|
33
34
|
const FIXTURES_DIR = join(import.meta.dir, '../hooks/test-fixtures');
|
|
@@ -97,9 +98,9 @@ describe('restore migration failure reporting (celilo#1269)', () => {
|
|
|
97
98
|
|
|
98
99
|
afterEach(() => {
|
|
99
100
|
closeDb();
|
|
100
|
-
|
|
101
|
-
process.env.CELILO_DATA_DIR
|
|
102
|
-
process.env.CELILO_MASTER_KEY_PATH
|
|
101
|
+
resetTestDbPath();
|
|
102
|
+
delete process.env.CELILO_DATA_DIR;
|
|
103
|
+
delete process.env.CELILO_MASTER_KEY_PATH;
|
|
103
104
|
try {
|
|
104
105
|
rmSync(dir, { recursive: true, force: true });
|
|
105
106
|
} catch {
|
|
@@ -92,11 +92,13 @@ export const ALL_CATEGORIES: readonly DriftCategory[] = [
|
|
|
92
92
|
'interface_classification',
|
|
93
93
|
'module_integrity',
|
|
94
94
|
'detect_without_converge',
|
|
95
|
+
'jail_exemptions',
|
|
95
96
|
];
|
|
96
97
|
|
|
97
98
|
export const CATEGORY_LABELS: Record<DriftCategory, string> = {
|
|
98
99
|
module_integrity: 'Module integrity',
|
|
99
100
|
detect_without_converge: 'Drift without converge',
|
|
101
|
+
jail_exemptions: 'Hook jail exemptions',
|
|
100
102
|
interface_classification: 'Firewall interfaces',
|
|
101
103
|
cli_version: 'CLI version',
|
|
102
104
|
schema: 'Schema migrations',
|
package/src/config/paths.test.ts
CHANGED
|
@@ -21,14 +21,14 @@ describe('paths configuration', () => {
|
|
|
21
21
|
describe('getModuleStoragePath', () => {
|
|
22
22
|
it('returns custom path when CELILO_DATA_DIR is set', () => {
|
|
23
23
|
process.env.CELILO_DATA_DIR = '/custom/data';
|
|
24
|
-
process.env.ENVIRONMENT
|
|
24
|
+
delete process.env.ENVIRONMENT;
|
|
25
25
|
|
|
26
26
|
const path = getModuleStoragePath();
|
|
27
27
|
expect(path).toBe('/custom/data/modules');
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
it('returns dev path when ENVIRONMENT=dev', () => {
|
|
31
|
-
process.env.CELILO_DATA_DIR
|
|
31
|
+
delete process.env.CELILO_DATA_DIR;
|
|
32
32
|
process.env.ENVIRONMENT = 'dev';
|
|
33
33
|
|
|
34
34
|
const path = getModuleStoragePath();
|
|
@@ -38,8 +38,8 @@ describe('paths configuration', () => {
|
|
|
38
38
|
it.skipIf(skipIntegration({ platform: 'darwin' }))(
|
|
39
39
|
'returns platform-specific path by default',
|
|
40
40
|
() => {
|
|
41
|
-
process.env.CELILO_DATA_DIR
|
|
42
|
-
process.env.ENVIRONMENT
|
|
41
|
+
delete process.env.CELILO_DATA_DIR;
|
|
42
|
+
delete process.env.ENVIRONMENT;
|
|
43
43
|
|
|
44
44
|
const path = getModuleStoragePath();
|
|
45
45
|
|
|
@@ -63,14 +63,14 @@ describe('paths configuration', () => {
|
|
|
63
63
|
describe('getDataDir', () => {
|
|
64
64
|
it('returns custom path when CELILO_DATA_DIR is set', () => {
|
|
65
65
|
process.env.CELILO_DATA_DIR = '/custom/data';
|
|
66
|
-
process.env.ENVIRONMENT
|
|
66
|
+
delete process.env.ENVIRONMENT;
|
|
67
67
|
|
|
68
68
|
const path = getDataDir();
|
|
69
69
|
expect(path).toBe('/custom/data');
|
|
70
70
|
});
|
|
71
71
|
|
|
72
72
|
it('returns dev path when ENVIRONMENT=dev', () => {
|
|
73
|
-
process.env.CELILO_DATA_DIR
|
|
73
|
+
delete process.env.CELILO_DATA_DIR;
|
|
74
74
|
process.env.ENVIRONMENT = 'dev';
|
|
75
75
|
|
|
76
76
|
const path = getDataDir();
|
|
@@ -80,8 +80,8 @@ describe('paths configuration', () => {
|
|
|
80
80
|
it.skipIf(skipIntegration({ platform: 'darwin' }))(
|
|
81
81
|
'returns platform-specific path by default',
|
|
82
82
|
() => {
|
|
83
|
-
process.env.CELILO_DATA_DIR
|
|
84
|
-
process.env.ENVIRONMENT
|
|
83
|
+
delete process.env.CELILO_DATA_DIR;
|
|
84
|
+
delete process.env.ENVIRONMENT;
|
|
85
85
|
|
|
86
86
|
const path = getDataDir();
|
|
87
87
|
|
|
@@ -103,7 +103,7 @@ describe('paths configuration', () => {
|
|
|
103
103
|
});
|
|
104
104
|
|
|
105
105
|
it('returns data dir + master.key when CELILO_MASTER_KEY_PATH not set', () => {
|
|
106
|
-
process.env.CELILO_MASTER_KEY_PATH
|
|
106
|
+
delete process.env.CELILO_MASTER_KEY_PATH;
|
|
107
107
|
process.env.CELILO_DATA_DIR = '/custom/data';
|
|
108
108
|
|
|
109
109
|
const path = getMasterKeyPath();
|
|
@@ -113,9 +113,9 @@ describe('paths configuration', () => {
|
|
|
113
113
|
it.skipIf(skipIntegration({ platform: 'darwin' }))(
|
|
114
114
|
'uses platform-specific data dir by default',
|
|
115
115
|
() => {
|
|
116
|
-
process.env.CELILO_MASTER_KEY_PATH
|
|
117
|
-
process.env.CELILO_DATA_DIR
|
|
118
|
-
process.env.ENVIRONMENT
|
|
116
|
+
delete process.env.CELILO_MASTER_KEY_PATH;
|
|
117
|
+
delete process.env.CELILO_DATA_DIR;
|
|
118
|
+
delete process.env.ENVIRONMENT;
|
|
119
119
|
|
|
120
120
|
const path = getMasterKeyPath();
|
|
121
121
|
|
|
@@ -131,8 +131,8 @@ describe('paths configuration', () => {
|
|
|
131
131
|
describe('getDbPath', () => {
|
|
132
132
|
it('returns custom path when CELILO_DB_PATH is set', () => {
|
|
133
133
|
process.env.CELILO_DB_PATH = '/custom/path/celilo.db';
|
|
134
|
-
process.env.CELILO_DATA_DIR
|
|
135
|
-
process.env.ENVIRONMENT
|
|
134
|
+
delete process.env.CELILO_DATA_DIR;
|
|
135
|
+
delete process.env.ENVIRONMENT;
|
|
136
136
|
|
|
137
137
|
const path = getDbPath();
|
|
138
138
|
expect(path).toBe('/custom/path/celilo.db');
|
|
@@ -141,9 +141,9 @@ describe('paths configuration', () => {
|
|
|
141
141
|
it.skipIf(skipIntegration({ platform: 'darwin' }))(
|
|
142
142
|
'returns data dir + celilo.db on macOS in production',
|
|
143
143
|
() => {
|
|
144
|
-
process.env.CELILO_DB_PATH
|
|
145
|
-
process.env.CELILO_DATA_DIR
|
|
146
|
-
process.env.ENVIRONMENT
|
|
144
|
+
delete process.env.CELILO_DB_PATH;
|
|
145
|
+
delete process.env.CELILO_DATA_DIR;
|
|
146
|
+
delete process.env.ENVIRONMENT;
|
|
147
147
|
|
|
148
148
|
const path = getDbPath();
|
|
149
149
|
|
|
@@ -156,8 +156,8 @@ describe('paths configuration', () => {
|
|
|
156
156
|
);
|
|
157
157
|
|
|
158
158
|
it('returns celilo-data/celilo.db in development mode', () => {
|
|
159
|
-
process.env.CELILO_DB_PATH
|
|
160
|
-
process.env.CELILO_DATA_DIR
|
|
159
|
+
delete process.env.CELILO_DB_PATH;
|
|
160
|
+
delete process.env.CELILO_DATA_DIR;
|
|
161
161
|
process.env.ENVIRONMENT = 'dev';
|
|
162
162
|
|
|
163
163
|
const path = getDbPath();
|
|
@@ -165,9 +165,9 @@ describe('paths configuration', () => {
|
|
|
165
165
|
});
|
|
166
166
|
|
|
167
167
|
it('respects CELILO_DATA_DIR override', () => {
|
|
168
|
-
process.env.CELILO_DB_PATH
|
|
168
|
+
delete process.env.CELILO_DB_PATH;
|
|
169
169
|
process.env.CELILO_DATA_DIR = '/custom/data';
|
|
170
|
-
process.env.ENVIRONMENT
|
|
170
|
+
delete process.env.ENVIRONMENT;
|
|
171
171
|
|
|
172
172
|
const path = getDbPath();
|
|
173
173
|
expect(path).toBe('/custom/data/celilo.db');
|
|
@@ -176,7 +176,7 @@ describe('paths configuration', () => {
|
|
|
176
176
|
it('prioritizes CELILO_DB_PATH over CELILO_DATA_DIR', () => {
|
|
177
177
|
process.env.CELILO_DB_PATH = '/explicit/db.db';
|
|
178
178
|
process.env.CELILO_DATA_DIR = '/custom/data';
|
|
179
|
-
process.env.ENVIRONMENT
|
|
179
|
+
delete process.env.ENVIRONMENT;
|
|
180
180
|
|
|
181
181
|
const path = getDbPath();
|
|
182
182
|
expect(path).toBe('/explicit/db.db');
|
package/src/db/client.test.ts
CHANGED
|
@@ -3,7 +3,8 @@ import { afterEach, describe, expect, test } from 'bun:test';
|
|
|
3
3
|
import { rmSync } from 'node:fs';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
|
-
import {
|
|
6
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
7
|
+
import { closeDb, createDbClient, getDb } from './client';
|
|
7
8
|
|
|
8
9
|
// #798: a command that opened the db while a deploy still held it died on
|
|
9
10
|
// SQLITE_BUSY immediately, because the connection carried no busy timeout.
|
|
@@ -61,3 +62,47 @@ describe('createDbClient concurrency', () => {
|
|
|
61
62
|
}
|
|
62
63
|
});
|
|
63
64
|
});
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The tripwire from celilo#1343: under bun test, a default-path open must be
|
|
68
|
+
* refused instead of landing on the operator's real database. The scratch-DB
|
|
69
|
+
* preload (apps/celilo/test-preload.ts) arms CELILO_DB_PATH only when bun
|
|
70
|
+
* runs from apps/celilo/, so every one of these tests strips the redirects
|
|
71
|
+
* itself to reproduce the out-of-scope state.
|
|
72
|
+
*/
|
|
73
|
+
describe('createDbClient operator-database tripwire (celilo#1343)', () => {
|
|
74
|
+
afterEach(() => {
|
|
75
|
+
closeDb();
|
|
76
|
+
resetTestDbPath();
|
|
77
|
+
delete process.env.CELILO_DATA_DIR;
|
|
78
|
+
delete process.env.ENVIRONMENT;
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('default-path open under bun test throws, naming the fix', () => {
|
|
82
|
+
delete process.env.CELILO_DB_PATH;
|
|
83
|
+
delete process.env.CELILO_DATA_DIR;
|
|
84
|
+
delete process.env.ENVIRONMENT;
|
|
85
|
+
|
|
86
|
+
expect(() => getDb()).toThrow(/CELILO_DB_PATH/);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('CELILO_DATA_DIR redirect keeps the default-path open allowed', () => {
|
|
90
|
+
delete process.env.CELILO_DB_PATH;
|
|
91
|
+
delete process.env.ENVIRONMENT;
|
|
92
|
+
process.env.CELILO_DATA_DIR = join(tmpdir(), 'celilo-tripwire-redirect');
|
|
93
|
+
|
|
94
|
+
const db = createDbClient();
|
|
95
|
+
db.$client.close();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('explicit config path bypasses the tripwire', () => {
|
|
99
|
+
delete process.env.CELILO_DB_PATH;
|
|
100
|
+
delete process.env.CELILO_DATA_DIR;
|
|
101
|
+
delete process.env.ENVIRONMENT;
|
|
102
|
+
|
|
103
|
+
const db = createDbClient({
|
|
104
|
+
path: join(tmpdir(), 'celilo-tripwire-explicit', 'celilo.db'),
|
|
105
|
+
});
|
|
106
|
+
db.$client.close();
|
|
107
|
+
});
|
|
108
|
+
});
|
package/src/db/client.ts
CHANGED
|
@@ -49,6 +49,32 @@ export function createDbClient(config?: Partial<DatabaseConfig>) {
|
|
|
49
49
|
const dbPath = config?.path ?? getDbPath();
|
|
50
50
|
const readonly = config?.readonly ?? false;
|
|
51
51
|
|
|
52
|
+
// Tripwire against opening the operator's real database from a test
|
|
53
|
+
// (celilo#1343). The scratch-DB preload in apps/celilo/test-preload.ts only
|
|
54
|
+
// arms when bun runs from apps/celilo/ — bun reads the bunfig at the working
|
|
55
|
+
// directory — so the same suite run from the repo root reaches this function
|
|
56
|
+
// with every override unset and would open, and migrate, the operator's
|
|
57
|
+
// ~/Library/.../celilo.db. The condition below is exactly that resolution:
|
|
58
|
+
// under bun test, with every redirect unset, getDbPath() falls through to
|
|
59
|
+
// the platform data directory. A run that is properly scoped (preload armed,
|
|
60
|
+
// CELILO_DATA_DIR redirected, or an explicit config.path) never satisfies
|
|
61
|
+
// it and pays one string comparison.
|
|
62
|
+
if (
|
|
63
|
+
!config?.path &&
|
|
64
|
+
process.env.NODE_ENV === 'test' &&
|
|
65
|
+
!process.env.CELILO_DB_PATH &&
|
|
66
|
+
!process.env.CELILO_DATA_DIR &&
|
|
67
|
+
process.env.ENVIRONMENT !== 'dev'
|
|
68
|
+
) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
'Refusing to open the celilo database under bun test with no database path set ' +
|
|
71
|
+
'(celilo#1343): the scratch-DB preload in apps/celilo/test-preload.ts only arms ' +
|
|
72
|
+
"when bun runs from apps/celilo/, so this default path is the operator's real " +
|
|
73
|
+
'celilo.db. Run tests from apps/celilo/, or set CELILO_DB_PATH (or ' +
|
|
74
|
+
'CELILO_DATA_DIR) to a test-scoped path.',
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
52
78
|
// bun:sqlite's `create: true` makes the file but not the parent directory.
|
|
53
79
|
// First-run after a fresh install hits this — without recursive mkdir we
|
|
54
80
|
// get SQLITE_CANTOPEN before init can write its config.
|
package/src/db/migrate.test.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { describe, expect, it } from 'bun:test';
|
|
|
20
20
|
import { mkdtempSync, rmSync } from 'node:fs';
|
|
21
21
|
import { tmpdir } from 'node:os';
|
|
22
22
|
import { join } from 'node:path';
|
|
23
|
+
import { resetTestDbPath } from '../test-utils/db-path';
|
|
23
24
|
import { closeDb, getDb } from './client';
|
|
24
25
|
import { runMigrations } from './migrate';
|
|
25
26
|
import { systemConfig } from './schema';
|
|
@@ -48,7 +49,7 @@ describe('runMigrations connection lifecycle', () => {
|
|
|
48
49
|
closeDb();
|
|
49
50
|
} finally {
|
|
50
51
|
closeDb();
|
|
51
|
-
|
|
52
|
+
resetTestDbPath();
|
|
52
53
|
rmSync(dir, { recursive: true, force: true });
|
|
53
54
|
}
|
|
54
55
|
});
|
package/src/db/migrate.ts
CHANGED
|
@@ -80,26 +80,26 @@ export function runMigrationsOn(db: DbClient): void {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
|
-
*
|
|
84
|
-
*
|
|
83
|
+
* Open a database at `dbPath` with migrations applied, then close it.
|
|
84
|
+
* Standalone entrypoint (`bun run src/db/migrate.ts`); tests also use it to
|
|
85
|
+
* prepare a fresh scratch database.
|
|
86
|
+
*
|
|
87
|
+
* createDbClient migrates synchronously on open (its own runMigrationsOn
|
|
88
|
+
* call), so this function is open + close. It deliberately does NOT call
|
|
89
|
+
* runMigrationsOn again: a second pass is always a ledger no-op when the
|
|
90
|
+
* first succeeded, and keeping it invited a double-migrate everywhere this
|
|
91
|
+
* entrypoint is used (celilo#1315).
|
|
85
92
|
*/
|
|
86
93
|
export async function runMigrations(dbPath?: string) {
|
|
87
94
|
console.log('Running database migrations...');
|
|
88
95
|
const db = createDbClient(dbPath ? { path: dbPath } : undefined);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
// Close the connection THIS call created. It is not the singleton —
|
|
97
|
-
// createDbClient does not register one — so the old closeDb() here closed
|
|
98
|
-
// whatever else was open (usually nothing) and leaked this connection,
|
|
99
|
-
// which then held the file in WAL and blocked every later journal-mode
|
|
100
|
-
// switch on it (celilo#1269).
|
|
101
|
-
db.$client.close();
|
|
102
|
-
}
|
|
96
|
+
console.log('Migrations completed successfully');
|
|
97
|
+
// Close the connection THIS call created. It is not the singleton —
|
|
98
|
+
// createDbClient does not register one — so an old closeDb() here closed
|
|
99
|
+
// whatever else was open (usually nothing) and leaked this connection,
|
|
100
|
+
// which then held the file in WAL and blocked every later journal-mode
|
|
101
|
+
// switch on it (celilo#1269).
|
|
102
|
+
db.$client.close();
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
// Run migrations if executed directly
|
package/src/db/schema.ts
CHANGED
|
@@ -212,6 +212,45 @@ export const capabilityBindings = sqliteTable(
|
|
|
212
212
|
}),
|
|
213
213
|
);
|
|
214
214
|
|
|
215
|
+
/**
|
|
216
|
+
* One build-bus `on_upstream_publish` hook execution (services/build-bus/hook-dispatcher.ts).
|
|
217
|
+
*
|
|
218
|
+
* The self-update path had no durable record: outcomes were console output
|
|
219
|
+
* inside the receiver daemon, so a CLI that never upgraded looked like a
|
|
220
|
+
* healthy fleet (celilo#1304). One row per (event, module, hook) run — the
|
|
221
|
+
* newest row answers "did the self-update for @celilo/cli@X fire and succeed?".
|
|
222
|
+
*
|
|
223
|
+
* `exit_code` is NULL when the script never ran to an exit (spawn failure).
|
|
224
|
+
* Not an event-bus delivery: hooks run from the receiver's in-process
|
|
225
|
+
* dispatcher, which no `deliveries` row was ever written for.
|
|
226
|
+
*
|
|
227
|
+
* @owner celilo — build-bus self-update audit trail (celilo#1304)
|
|
228
|
+
*/
|
|
229
|
+
export const buildBusHookRuns = sqliteTable(
|
|
230
|
+
'build_bus_hook_runs',
|
|
231
|
+
{
|
|
232
|
+
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
233
|
+
/** The PublishEvent's dedup id — joins back to the bus's `build-bus.publish` event. */
|
|
234
|
+
eventId: text('event_id').notNull(),
|
|
235
|
+
packageName: text('package_name').notNull(),
|
|
236
|
+
packageVersion: text('package_version').notNull(),
|
|
237
|
+
tag: text('tag').notNull(),
|
|
238
|
+
moduleId: text('module_id').notNull(),
|
|
239
|
+
hookName: text('hook_name').notNull(),
|
|
240
|
+
scriptPath: text('script_path').notNull(),
|
|
241
|
+
/** Null means the script never reached an exit (spawn failure). */
|
|
242
|
+
exitCode: integer('exit_code'),
|
|
243
|
+
timedOut: integer('timed_out', { mode: 'boolean' }).notNull().default(false),
|
|
244
|
+
durationMs: integer('duration_ms').notNull(),
|
|
245
|
+
stdoutTail: text('stdout_tail'),
|
|
246
|
+
stderrTail: text('stderr_tail'),
|
|
247
|
+
ranAt: integer('created_at', { mode: 'timestamp' }).notNull().default(sql`(unixepoch())`),
|
|
248
|
+
},
|
|
249
|
+
(table) => ({
|
|
250
|
+
ranAtLookup: index('build_bus_hook_runs_created_at_idx').on(table.ranAt),
|
|
251
|
+
}),
|
|
252
|
+
);
|
|
253
|
+
|
|
215
254
|
/**
|
|
216
255
|
* Capability secrets table - stores encrypted secrets owned by capabilities
|
|
217
256
|
* Values are encrypted with AES-256-GCM using master key
|
|
@@ -310,6 +349,28 @@ export const moduleIntegrity = sqliteTable('module_integrity', {
|
|
|
310
349
|
updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull().default(sql`(unixepoch())`),
|
|
311
350
|
});
|
|
312
351
|
|
|
352
|
+
/**
|
|
353
|
+
* Per-module hook jail policy — the operator's row in the four-step
|
|
354
|
+
* precedence (per-module-jail-policy task 1.1), ranked between the
|
|
355
|
+
* `CELILO_HOOK_JAIL` environment variable and the system-wide
|
|
356
|
+
* `hooks.jail_policy` key.
|
|
357
|
+
*
|
|
358
|
+
* One row per module; an absent row means "follow the system", so there is
|
|
359
|
+
* no default column to get wrong. The value is validated at set time by the
|
|
360
|
+
* `module jail` verb and re-validated at read time by `resolveJailPolicy`,
|
|
361
|
+
* which throws rather than coercing — a restored or hand-edited row outside
|
|
362
|
+
* the accepted set must fail loudly, the same rule the system key follows
|
|
363
|
+
* (peba's ruling on ce-8832).
|
|
364
|
+
* @owner celilo — operator-set per-module jail policy
|
|
365
|
+
*/
|
|
366
|
+
export const moduleJailPolicies = sqliteTable('module_jail_policies', {
|
|
367
|
+
moduleId: text('module_id')
|
|
368
|
+
.primaryKey()
|
|
369
|
+
.references(() => modules.id, { onDelete: 'cascade' }),
|
|
370
|
+
policy: text('policy').$type<'auto' | 'off' | 'required'>().notNull(),
|
|
371
|
+
updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull().default(sql`(unixepoch())`),
|
|
372
|
+
});
|
|
373
|
+
|
|
313
374
|
/**
|
|
314
375
|
* Lifecycle states an instance moves through.
|
|
315
376
|
*
|
|
@@ -1579,3 +1640,5 @@ export type NotificationDelivery = typeof notificationDeliveries.$inferSelect;
|
|
|
1579
1640
|
export type NewNotificationDelivery = typeof notificationDeliveries.$inferInsert;
|
|
1580
1641
|
export type CapabilityBinding = typeof capabilityBindings.$inferSelect;
|
|
1581
1642
|
export type NewCapabilityBinding = typeof capabilityBindings.$inferInsert;
|
|
1643
|
+
export type BuildBusHookRun = typeof buildBusHookRuns.$inferSelect;
|
|
1644
|
+
export type NewBuildBusHookRun = typeof buildBusHookRuns.$inferInsert;
|