@noodleseed/one 0.170.0 → 0.171.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/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +3 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/catalog.d.ts.map +1 -1
- package/dist/commands/catalog.js +2 -1
- package/dist/commands/catalog.js.map +1 -1
- package/dist/commands/whatsapp-ops.d.ts +13 -0
- package/dist/commands/whatsapp-ops.d.ts.map +1 -0
- package/dist/commands/whatsapp-ops.js +244 -0
- package/dist/commands/whatsapp-ops.js.map +1 -0
- package/dist/commands/whatsapp-output.d.ts +3 -0
- package/dist/commands/whatsapp-output.d.ts.map +1 -0
- package/dist/commands/whatsapp-output.js +70 -0
- package/dist/commands/whatsapp-output.js.map +1 -0
- package/dist/dev.js +2 -0
- package/dist/dev.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/plugin-mode/plugin-operation-contract.d.ts +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/curated/command-groups.js +4 -0
- package/node_modules/@noodle-borg/agent-kit/dist/curated/error-fixes.js +1 -0
- package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js +3 -0
- package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.js +6 -0
- package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
- package/node_modules/@noodle-borg/app-audit/dist/audit-embedded-assistant.js +2 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-configuration.js +2 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-model-context.js +9 -4
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-turn-context.d.ts +16 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-turn-context.js +19 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.d.ts +145 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.js +378 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-inbox.js +160 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-provider-blocks.d.ts +18 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-provider-blocks.js +69 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-spend.js +82 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-store.d.ts +42 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-store.js +76 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-types.d.ts +134 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-types.js +72 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-work.d.ts +20 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-work.js +221 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/messaging-tool-dispatch.d.ts +15 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/messaging-tool-dispatch.js +34 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-request.d.ts +19 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-request.js +22 -6
- package/node_modules/@noodle-borg/assistant-gateway/dist/portable.d.ts +7 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/portable.js +7 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant-appearance-store.js +83 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant-elevation.js +151 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant-interaction-writes.js +45 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant-interactions.js +340 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-assistant.js +439 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-channel-store.js +102 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-continuity-store.js +159 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-elevation-store.js +110 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres-embed-store.js +134 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/postgres.js +11 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/public-surface.d.ts +8 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/public-surface.js +23 -0
- package/node_modules/@noodle-borg/authoring/dist/assistant.d.ts +17 -2
- package/node_modules/@noodle-borg/authoring/dist/assistant.js +48 -19
- package/node_modules/@noodle-borg/authoring/dist/index.d.ts +1 -1
- package/node_modules/@noodle-borg/authoring/dist/index.js +1 -1
- package/node_modules/@noodle-borg/authoring/dist/load-authored.js +1 -1
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-channels.js +121 -0
- package/node_modules/@noodle-borg/cli-catalog/dist/index.js +1 -0
- package/node_modules/@noodle-borg/compiler/dist/artifact/config.js +1 -1
- package/node_modules/@noodle-borg/compiler/dist/errors.d.ts +1 -1
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +72 -12
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +51 -27
- package/node_modules/@noodle-borg/compiler/dist/manifest/website-projection.d.ts +1 -0
- package/node_modules/@noodle-borg/compiler/dist/manifest/website-projection.js +19 -0
- package/node_modules/@noodle-borg/module-audit/dist/index.js +37 -0
- package/node_modules/@noodle-borg/module-audit/dist/postgres-audit.js +126 -0
- package/node_modules/@noodle-borg/module-audit/dist/route.js +66 -0
- package/node_modules/@noodle-borg/runtime/dist/managed-origins.js +8 -4
- package/node_modules/@noodle-borg/runtime/dist/postgres-state-handles.js +311 -0
- package/node_modules/@noodle-borg/runtime/dist/postgres.js +4 -0
- package/node_modules/@noodle-borg/service/dist/assistant-composition.js +18 -0
- package/node_modules/@noodle-borg/service/dist/build-info.js +5 -2
- package/node_modules/@noodle-borg/service/dist/business-information/source-ingestion-coordinator.js +7 -0
- package/node_modules/@noodle-borg/service/dist/channels/360dialog.js +233 -0
- package/node_modules/@noodle-borg/service/dist/channels/cipher.js +37 -0
- package/node_modules/@noodle-borg/service/dist/channels/composition.js +37 -0
- package/node_modules/@noodle-borg/service/dist/channels/inference-guard.js +53 -0
- package/node_modules/@noodle-borg/service/dist/channels/runtime.js +337 -0
- package/node_modules/@noodle-borg/service/dist/channels/worker-loop.js +50 -0
- package/node_modules/@noodle-borg/service/dist/routes/assistant-agent.js +62 -34
- package/node_modules/@noodle-borg/service/dist/routes/assistant-model-binding.js +6 -2
- package/node_modules/@noodle-borg/service/dist/routes/business-information-channels.js +14 -1
- package/node_modules/@noodle-borg/service/dist/routes/whatsapp.js +238 -0
- package/node_modules/@noodle-borg/service/dist/service.js +30 -32
- package/node_modules/@noodle-borg/service/package.json +1 -1
- package/node_modules/@noodle-borg/wire-contracts/dist/application-channels.d.ts +33 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/application-channels.js +9 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/index.d.ts +1 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/index.js +1 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/service-info.d.ts +2 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/service-info.js +10 -2
- package/node_modules/@noodle-borg/wire-contracts/dist/whatsapp.d.ts +426 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/whatsapp.js +213 -0
- package/package.json +2 -2
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
const flag = (name, summary, type = 'string') => ({
|
|
2
|
+
name,
|
|
3
|
+
summary,
|
|
4
|
+
type,
|
|
5
|
+
...(type === 'boolean' ? {} : { value: '<value>' }),
|
|
6
|
+
required: false,
|
|
7
|
+
repeatable: false,
|
|
8
|
+
sensitive: name === 'auth-token',
|
|
9
|
+
aliases: [],
|
|
10
|
+
conflictsWith: [],
|
|
11
|
+
});
|
|
12
|
+
const scope = [
|
|
13
|
+
flag('org', 'Organization.'),
|
|
14
|
+
flag('app', 'Application.'),
|
|
15
|
+
flag('env', 'Environment.'),
|
|
16
|
+
flag('service', 'Service URL.'),
|
|
17
|
+
flag('auth-token', 'Control-plane token.'),
|
|
18
|
+
flag('json', 'Machine-readable output.', 'boolean'),
|
|
19
|
+
];
|
|
20
|
+
const mutation = [flag('idempotency-key', 'Stable key for retrying this logical change.')];
|
|
21
|
+
const revision = flag('expected-revision', 'Current revision from status; zero when creating.', 'number');
|
|
22
|
+
const blockScope = flag('scope', 'local (default) or explicit provider block.');
|
|
23
|
+
const participant = flag('participant-id', 'Protected participant identifier from events list.');
|
|
24
|
+
const limits = [
|
|
25
|
+
'per-minute',
|
|
26
|
+
'per-hour',
|
|
27
|
+
'per-day',
|
|
28
|
+
'channel-per-day',
|
|
29
|
+
'new-participants-per-day',
|
|
30
|
+
'concurrent',
|
|
31
|
+
'pending-per-participant',
|
|
32
|
+
'pending',
|
|
33
|
+
'text-characters',
|
|
34
|
+
'daily-micro-usd',
|
|
35
|
+
].map((name) => flag(name, `Lower the ${name} ceiling; zero disables this allowance.`, 'number'));
|
|
36
|
+
const leaf = (name, summary, flags = []) => ({
|
|
37
|
+
name,
|
|
38
|
+
summary,
|
|
39
|
+
flags: [...scope, ...flags],
|
|
40
|
+
arguments: [],
|
|
41
|
+
jsonOutput: { mode: 'single' },
|
|
42
|
+
});
|
|
43
|
+
const group = (name, summary, subcommands) => ({ name, summary, subcommands, flags: [], arguments: [] });
|
|
44
|
+
export const CHANNELS_COMMAND = {
|
|
45
|
+
name: 'channels',
|
|
46
|
+
section: 'operate',
|
|
47
|
+
summary: 'Connect and operate the same application on messaging channels.',
|
|
48
|
+
arguments: [],
|
|
49
|
+
flags: [],
|
|
50
|
+
subcommands: [
|
|
51
|
+
group('whatsapp', 'Configure the 360dialog channel, test readiness, and control public messaging.', [
|
|
52
|
+
leaf('status', 'Show configuration and current revision.'),
|
|
53
|
+
leaf('configure', 'Save a paused binding using managed secret references.', [
|
|
54
|
+
revision,
|
|
55
|
+
...mutation,
|
|
56
|
+
flag('phone-number-id', '360dialog/Meta phone-number asset ID.'),
|
|
57
|
+
flag('api-key-secret', 'Managed secret name for the 360dialog key.'),
|
|
58
|
+
flag('webhook-secret', 'Independent managed callback secret name.'),
|
|
59
|
+
flag('capabilities', 'Comma-separated knowledge:name or tool:name selections.'),
|
|
60
|
+
flag('support-email', 'Contact shown when an answer cannot be completed.'),
|
|
61
|
+
...limits,
|
|
62
|
+
]),
|
|
63
|
+
leaf('doctor', 'Check deployment, storage, worker, model cost bound, and provider setup.', mutation),
|
|
64
|
+
leaf('enable', 'Enable after a successful current readiness check.', [
|
|
65
|
+
revision,
|
|
66
|
+
...mutation,
|
|
67
|
+
]),
|
|
68
|
+
leaf('pause', 'Stop new work and unsent replies.', [revision, ...mutation]),
|
|
69
|
+
leaf('disconnect', 'Fence this binding and retain governed receipts.', [
|
|
70
|
+
revision,
|
|
71
|
+
...mutation,
|
|
72
|
+
]),
|
|
73
|
+
group('webhook', 'Inspect or configure the owned provider callback.', [
|
|
74
|
+
leaf('inspect', 'Check URL and authentication without disclosing headers.'),
|
|
75
|
+
leaf('configure', 'Register the service-owned callback.', [revision, ...mutation]),
|
|
76
|
+
]),
|
|
77
|
+
group('limits', 'Inspect or lower the approved limits.', [
|
|
78
|
+
leaf('get', 'Show limits.'),
|
|
79
|
+
leaf('set', 'Lower selected limits.', [revision, ...mutation, ...limits]),
|
|
80
|
+
]),
|
|
81
|
+
leaf('usage', 'Show UTC-day settled and reserved inference spend.'),
|
|
82
|
+
group('blocks', 'Inspect local blocks.', [
|
|
83
|
+
leaf('list', 'List local and provider block evidence.', [
|
|
84
|
+
flag('after', 'Cursor from the previous page.'),
|
|
85
|
+
]),
|
|
86
|
+
]),
|
|
87
|
+
leaf('block', 'Block locally; optionally request a provider block.', [
|
|
88
|
+
participant,
|
|
89
|
+
blockScope,
|
|
90
|
+
flag('phone-stdin', 'Read a phone number securely from standard input.', 'boolean'),
|
|
91
|
+
flag('until', 'Expiry as an ISO timestamp.'),
|
|
92
|
+
flag('indefinite', 'Explicitly retain this block until removed.', 'boolean'),
|
|
93
|
+
...mutation,
|
|
94
|
+
]),
|
|
95
|
+
leaf('unblock', 'Remove an explicitly selected block without resetting usage.', [
|
|
96
|
+
participant,
|
|
97
|
+
blockScope,
|
|
98
|
+
flag('phone-stdin', 'Supply the number if retained participant context has expired.', 'boolean'),
|
|
99
|
+
...mutation,
|
|
100
|
+
]),
|
|
101
|
+
group('cooldown', 'Inspect or clear an automatic cooldown.', [
|
|
102
|
+
leaf('inspect', 'Show a participant cooldown.', [participant]),
|
|
103
|
+
leaf('clear', 'Clear cooldown without resetting quotas.', [participant, ...mutation]),
|
|
104
|
+
]),
|
|
105
|
+
group('events', 'Inspect content-free transport receipts.', [
|
|
106
|
+
leaf('list', 'List a page of events.', [
|
|
107
|
+
flag('after', 'Cursor returned by the previous page.'),
|
|
108
|
+
]),
|
|
109
|
+
leaf('inspect', 'Inspect one event.', [flag('event-id', 'Event identifier.')]),
|
|
110
|
+
leaf('reconcile', 'Read current receipt evidence without resending.', [
|
|
111
|
+
flag('event-id', 'Event identifier.'),
|
|
112
|
+
...mutation,
|
|
113
|
+
]),
|
|
114
|
+
]),
|
|
115
|
+
group('conversation', 'Erase conversation content.', [
|
|
116
|
+
leaf('forget', 'Erase participant context and message content while preserving safeguards.', [participant, ...mutation]),
|
|
117
|
+
]),
|
|
118
|
+
]),
|
|
119
|
+
],
|
|
120
|
+
};
|
|
121
|
+
//# sourceMappingURL=catalog-data-channels.js.map
|
|
@@ -4,6 +4,7 @@ export * from './catalog-data-auth-discovery.js';
|
|
|
4
4
|
export * from './catalog-data-billing.js';
|
|
5
5
|
export * from './catalog-data-bootstrap-authoring.js';
|
|
6
6
|
export * from './catalog-data-bootstrap-pre-auth.js';
|
|
7
|
+
export * from './catalog-data-channels.js';
|
|
7
8
|
export * from './catalog-data-config.js';
|
|
8
9
|
export * from './catalog-data-core.js';
|
|
9
10
|
export * from './catalog-data-distributions.js';
|
|
@@ -20,7 +20,7 @@ function collectVariablesFromManagedOrigins(manifest, out) {
|
|
|
20
20
|
const assistant = manifest.server.assistant;
|
|
21
21
|
const values = [
|
|
22
22
|
...(assistant?.allowedOrigins ?? []),
|
|
23
|
-
...(assistant?.surfaces?.flatMap((surface) => surface.origins) ?? []),
|
|
23
|
+
...(assistant?.surfaces?.flatMap((surface) => surface.kind === 'messaging' ? [] : surface.origins) ?? []),
|
|
24
24
|
...(manifest.handoff?.allowedDomains ?? []),
|
|
25
25
|
];
|
|
26
26
|
for (const value of values) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AppPackageArtifactV1 } from '@noodle-borg/app-package';
|
|
2
2
|
import type { RuntimeArtifact } from './artifact/types.js';
|
|
3
3
|
import type { PackagedAsset } from './assets.js';
|
|
4
|
-
export type CompileErrorCode = 'invalid_context_provider' | 'yaml_parse_error' | 'invalid_shape' | 'invalid_name' | 'duplicate_name' | 'reserved_name' | 'unsupported_manifest_version' | 'reserved_for_future_version' | 'invalid_operation_ref' | 'external_ref' | 'invalid_schema_ref' | 'unknown_schema_ref' | 'schema_ref_conflict' | 'invalid_expression' | 'expr_unknown_root' | 'expr_root_unavailable' | 'expr_operator_not_allowed' | 'expr_if_not_boolean' | 'unknown_step_ref' | 'forward_step_ref' | 'self_step_ref' | 'duplicate_step_id' | 'invalid_fulfilment' | 'invalid_elicitation_schema' | 'invalid_elicitation_flow' | 'invalid_confirmation_flow' | 'arg_type_mismatch' | 'ambient_context_action' | 'duplicate_resource' | 'duplicate_prompt' | 'duplicate_resource_uri' | 'unsupported_uri_template' | 'duplicate_widget' | 'unknown_widget_tool' | 'unknown_widget_action_tool' | 'duplicate_widget_tool' | 'invalid_widget_binding' | 'invalid_widget_state_handle' | 'widget_html_too_large' | 'widget_html_total_too_large' | 'invalid_asset' | 'invalid_capability_requirement' | 'state_secret_field' | 'invalid_knowledge' | 'knowledge_unhashed' | 'invalid_managed_collection' | 'invalid_variable_declaration' | 'invalid_managed_collection_source' | 'unknown_connector_alias' | 'connector_not_in_catalog' | 'unknown_operation' | 'connector_binding_required' | 'ambiguous_nested_connector_binding' | 'invalid_connector_call_graph' | 'unsupported_credential_profile' | 'credential_scope_mismatch' | 'credential_audience_mismatch' | 'customer_endpoint_auth_required' | 'customer_endpoint_mapping_required' | 'customer_endpoint_unknown_mapping' | 'customer_endpoint_bridge_unsupported' | 'assistant_capability_unknown' | 'assistant_public_user_reference' | 'assistant_public_effect_unconfirmed' | 'customer_endpoint_action_unsupported' | 'customer_endpoint_surface_unsupported' | 'customer_endpoint_credential_source_unsupported' | 'customer_endpoint_policy_conflict' | 'customer_endpoint_routing_inconsistent' | 'unused_connector_alias' | 'arg_mismatch' | 'agent_guide_invalid' | 'agent_guide_duplicate_workflow' | 'agent_guide_duplicate_example' | 'agent_guide_example_workflow_missing' | 'agent_guide_capability_missing' | 'agent_guide_capability_kind' | 'app_package_sensitive_content';
|
|
4
|
+
export type CompileErrorCode = 'invalid_context_provider' | 'yaml_parse_error' | 'invalid_shape' | 'invalid_name' | 'duplicate_name' | 'reserved_name' | 'unsupported_manifest_version' | 'reserved_for_future_version' | 'invalid_operation_ref' | 'external_ref' | 'invalid_schema_ref' | 'unknown_schema_ref' | 'schema_ref_conflict' | 'invalid_expression' | 'expr_unknown_root' | 'expr_root_unavailable' | 'expr_operator_not_allowed' | 'expr_if_not_boolean' | 'unknown_step_ref' | 'forward_step_ref' | 'self_step_ref' | 'duplicate_step_id' | 'invalid_fulfilment' | 'invalid_elicitation_schema' | 'invalid_elicitation_flow' | 'invalid_confirmation_flow' | 'arg_type_mismatch' | 'ambient_context_action' | 'duplicate_resource' | 'duplicate_prompt' | 'duplicate_resource_uri' | 'unsupported_uri_template' | 'duplicate_widget' | 'unknown_widget_tool' | 'unknown_widget_action_tool' | 'duplicate_widget_tool' | 'invalid_widget_binding' | 'invalid_widget_state_handle' | 'widget_html_too_large' | 'widget_html_total_too_large' | 'invalid_asset' | 'invalid_capability_requirement' | 'state_secret_field' | 'invalid_knowledge' | 'knowledge_unhashed' | 'invalid_managed_collection' | 'invalid_variable_declaration' | 'invalid_managed_collection_source' | 'unknown_connector_alias' | 'connector_not_in_catalog' | 'unknown_operation' | 'connector_binding_required' | 'ambiguous_nested_connector_binding' | 'invalid_connector_call_graph' | 'unsupported_credential_profile' | 'credential_scope_mismatch' | 'credential_audience_mismatch' | 'customer_endpoint_auth_required' | 'customer_endpoint_mapping_required' | 'customer_endpoint_unknown_mapping' | 'customer_endpoint_bridge_unsupported' | 'assistant_messaging_unsupported' | 'assistant_capability_unknown' | 'assistant_public_user_reference' | 'assistant_public_effect_unconfirmed' | 'customer_endpoint_action_unsupported' | 'customer_endpoint_surface_unsupported' | 'customer_endpoint_credential_source_unsupported' | 'customer_endpoint_policy_conflict' | 'customer_endpoint_routing_inconsistent' | 'unused_connector_alias' | 'arg_mismatch' | 'agent_guide_invalid' | 'agent_guide_duplicate_workflow' | 'agent_guide_duplicate_example' | 'agent_guide_example_workflow_missing' | 'agent_guide_capability_missing' | 'agent_guide_capability_kind' | 'app_package_sensitive_content';
|
|
5
5
|
export interface CompileError {
|
|
6
6
|
readonly code: CompileErrorCode;
|
|
7
7
|
/** Dotted path to the offending location (e.g. `tools.1.name`); empty string for the document root. */
|
|
@@ -358,10 +358,25 @@ export declare const manifestV1Schema: z.ZodObject<{
|
|
|
358
358
|
kind: z.ZodLiteral<"noodle-managed">;
|
|
359
359
|
}, z.core.$strict>]>;
|
|
360
360
|
allowedOrigins: z.ZodArray<z.ZodString>;
|
|
361
|
-
surfaces: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
361
|
+
surfaces: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
362
|
+
kind: z.ZodLiteral<"messaging">;
|
|
363
|
+
channel: z.ZodLiteral<"whatsapp">;
|
|
364
|
+
mode: z.ZodLiteral<"public">;
|
|
365
|
+
capabilities: z.ZodArray<z.ZodObject<{
|
|
366
|
+
kind: z.ZodEnum<{
|
|
367
|
+
tool: "tool";
|
|
368
|
+
resource: "resource";
|
|
369
|
+
prompt: "prompt";
|
|
370
|
+
knowledge: "knowledge";
|
|
371
|
+
}>;
|
|
372
|
+
name: z.ZodString;
|
|
373
|
+
}, z.core.$strict>>;
|
|
374
|
+
instructions: z.ZodOptional<z.ZodString>;
|
|
375
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
376
|
+
kind: z.ZodOptional<z.ZodLiteral<"website">>;
|
|
362
377
|
mode: z.ZodEnum<{
|
|
363
|
-
authenticated: "authenticated";
|
|
364
378
|
public: "public";
|
|
379
|
+
authenticated: "authenticated";
|
|
365
380
|
mixed: "mixed";
|
|
366
381
|
}>;
|
|
367
382
|
origins: z.ZodArray<z.ZodString>;
|
|
@@ -386,7 +401,7 @@ export declare const manifestV1Schema: z.ZodObject<{
|
|
|
386
401
|
windowSeconds: z.ZodOptional<z.ZodNumber>;
|
|
387
402
|
maxRestores: z.ZodOptional<z.ZodNumber>;
|
|
388
403
|
}, z.core.$strict>>;
|
|
389
|
-
}, z.core.$strict>>>;
|
|
404
|
+
}, z.core.$strict>]>>>;
|
|
390
405
|
sessionClaims: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
391
406
|
exposeToModel: z.ZodOptional<z.ZodBoolean>;
|
|
392
407
|
}, z.core.$strict>>>;
|
|
@@ -847,10 +862,25 @@ export declare const manifestV2Schema: z.ZodObject<{
|
|
|
847
862
|
kind: z.ZodLiteral<"noodle-managed">;
|
|
848
863
|
}, z.core.$strict>]>;
|
|
849
864
|
allowedOrigins: z.ZodArray<z.ZodString>;
|
|
850
|
-
surfaces: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
865
|
+
surfaces: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
866
|
+
kind: z.ZodLiteral<"messaging">;
|
|
867
|
+
channel: z.ZodLiteral<"whatsapp">;
|
|
868
|
+
mode: z.ZodLiteral<"public">;
|
|
869
|
+
capabilities: z.ZodArray<z.ZodObject<{
|
|
870
|
+
kind: z.ZodEnum<{
|
|
871
|
+
tool: "tool";
|
|
872
|
+
resource: "resource";
|
|
873
|
+
prompt: "prompt";
|
|
874
|
+
knowledge: "knowledge";
|
|
875
|
+
}>;
|
|
876
|
+
name: z.ZodString;
|
|
877
|
+
}, z.core.$strict>>;
|
|
878
|
+
instructions: z.ZodOptional<z.ZodString>;
|
|
879
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
880
|
+
kind: z.ZodOptional<z.ZodLiteral<"website">>;
|
|
851
881
|
mode: z.ZodEnum<{
|
|
852
|
-
authenticated: "authenticated";
|
|
853
882
|
public: "public";
|
|
883
|
+
authenticated: "authenticated";
|
|
854
884
|
mixed: "mixed";
|
|
855
885
|
}>;
|
|
856
886
|
origins: z.ZodArray<z.ZodString>;
|
|
@@ -875,7 +905,7 @@ export declare const manifestV2Schema: z.ZodObject<{
|
|
|
875
905
|
windowSeconds: z.ZodOptional<z.ZodNumber>;
|
|
876
906
|
maxRestores: z.ZodOptional<z.ZodNumber>;
|
|
877
907
|
}, z.core.$strict>>;
|
|
878
|
-
}, z.core.$strict>>>;
|
|
908
|
+
}, z.core.$strict>]>>>;
|
|
879
909
|
sessionClaims: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
880
910
|
exposeToModel: z.ZodOptional<z.ZodBoolean>;
|
|
881
911
|
}, z.core.$strict>>>;
|
|
@@ -1602,10 +1632,25 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
1602
1632
|
kind: z.ZodLiteral<"noodle-managed">;
|
|
1603
1633
|
}, z.core.$strict>]>;
|
|
1604
1634
|
allowedOrigins: z.ZodArray<z.ZodString>;
|
|
1605
|
-
surfaces: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1635
|
+
surfaces: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
1636
|
+
kind: z.ZodLiteral<"messaging">;
|
|
1637
|
+
channel: z.ZodLiteral<"whatsapp">;
|
|
1638
|
+
mode: z.ZodLiteral<"public">;
|
|
1639
|
+
capabilities: z.ZodArray<z.ZodObject<{
|
|
1640
|
+
kind: z.ZodEnum<{
|
|
1641
|
+
tool: "tool";
|
|
1642
|
+
resource: "resource";
|
|
1643
|
+
prompt: "prompt";
|
|
1644
|
+
knowledge: "knowledge";
|
|
1645
|
+
}>;
|
|
1646
|
+
name: z.ZodString;
|
|
1647
|
+
}, z.core.$strict>>;
|
|
1648
|
+
instructions: z.ZodOptional<z.ZodString>;
|
|
1649
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1650
|
+
kind: z.ZodOptional<z.ZodLiteral<"website">>;
|
|
1606
1651
|
mode: z.ZodEnum<{
|
|
1607
|
-
authenticated: "authenticated";
|
|
1608
1652
|
public: "public";
|
|
1653
|
+
authenticated: "authenticated";
|
|
1609
1654
|
mixed: "mixed";
|
|
1610
1655
|
}>;
|
|
1611
1656
|
origins: z.ZodArray<z.ZodString>;
|
|
@@ -1630,7 +1675,7 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
1630
1675
|
windowSeconds: z.ZodOptional<z.ZodNumber>;
|
|
1631
1676
|
maxRestores: z.ZodOptional<z.ZodNumber>;
|
|
1632
1677
|
}, z.core.$strict>>;
|
|
1633
|
-
}, z.core.$strict>>>;
|
|
1678
|
+
}, z.core.$strict>]>>>;
|
|
1634
1679
|
sessionClaims: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
1635
1680
|
exposeToModel: z.ZodOptional<z.ZodBoolean>;
|
|
1636
1681
|
}, z.core.$strict>>>;
|
|
@@ -2090,10 +2135,25 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
2090
2135
|
kind: z.ZodLiteral<"noodle-managed">;
|
|
2091
2136
|
}, z.core.$strict>]>;
|
|
2092
2137
|
allowedOrigins: z.ZodArray<z.ZodString>;
|
|
2093
|
-
surfaces: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
2138
|
+
surfaces: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
2139
|
+
kind: z.ZodLiteral<"messaging">;
|
|
2140
|
+
channel: z.ZodLiteral<"whatsapp">;
|
|
2141
|
+
mode: z.ZodLiteral<"public">;
|
|
2142
|
+
capabilities: z.ZodArray<z.ZodObject<{
|
|
2143
|
+
kind: z.ZodEnum<{
|
|
2144
|
+
tool: "tool";
|
|
2145
|
+
resource: "resource";
|
|
2146
|
+
prompt: "prompt";
|
|
2147
|
+
knowledge: "knowledge";
|
|
2148
|
+
}>;
|
|
2149
|
+
name: z.ZodString;
|
|
2150
|
+
}, z.core.$strict>>;
|
|
2151
|
+
instructions: z.ZodOptional<z.ZodString>;
|
|
2152
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
2153
|
+
kind: z.ZodOptional<z.ZodLiteral<"website">>;
|
|
2094
2154
|
mode: z.ZodEnum<{
|
|
2095
|
-
authenticated: "authenticated";
|
|
2096
2155
|
public: "public";
|
|
2156
|
+
authenticated: "authenticated";
|
|
2097
2157
|
mixed: "mixed";
|
|
2098
2158
|
}>;
|
|
2099
2159
|
origins: z.ZodArray<z.ZodString>;
|
|
@@ -2118,7 +2178,7 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
2118
2178
|
windowSeconds: z.ZodOptional<z.ZodNumber>;
|
|
2119
2179
|
maxRestores: z.ZodOptional<z.ZodNumber>;
|
|
2120
2180
|
}, z.core.$strict>>;
|
|
2121
|
-
}, z.core.$strict>>>;
|
|
2181
|
+
}, z.core.$strict>]>>>;
|
|
2122
2182
|
sessionClaims: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
2123
2183
|
exposeToModel: z.ZodOptional<z.ZodBoolean>;
|
|
2124
2184
|
}, z.core.$strict>>>;
|
|
@@ -277,38 +277,50 @@ const embeddedAssistantSchema = z
|
|
|
277
277
|
.strict(),
|
|
278
278
|
z.object({ kind: z.literal('noodle-managed') }).strict(),
|
|
279
279
|
]),
|
|
280
|
-
allowedOrigins: z.array(assistantOriginOrVariableSchema)
|
|
280
|
+
allowedOrigins: z.array(assistantOriginOrVariableSchema),
|
|
281
281
|
// The front doors this assistant serves (ADR 0201, amended 2026-08-12). Absent means the pre-0201
|
|
282
282
|
// authenticated shape, so released artifacts keep their meaning; `allowedOrigins` stays the union
|
|
283
283
|
// and `sessionClaims` stays mirrored, so the service reads both unchanged. `capabilities` is the
|
|
284
284
|
// exact reachable surface: optional when authenticated, required when public or mixed.
|
|
285
285
|
surfaces: z
|
|
286
|
-
.array(z
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
286
|
+
.array(z.union([
|
|
287
|
+
z
|
|
288
|
+
.object({
|
|
289
|
+
kind: z.literal('messaging'),
|
|
290
|
+
channel: z.literal('whatsapp'),
|
|
291
|
+
mode: z.literal('public'),
|
|
292
|
+
capabilities: z.array(assistantCapabilitySchema),
|
|
293
|
+
instructions: z.string().trim().min(1).max(MAX_SERVER_INSTRUCTIONS).optional(),
|
|
294
|
+
})
|
|
295
|
+
.strict(),
|
|
296
|
+
z
|
|
297
|
+
.object({
|
|
298
|
+
kind: z.literal('website').optional(),
|
|
299
|
+
mode: z.enum(['authenticated', 'public', 'mixed']),
|
|
300
|
+
origins: z.array(assistantOriginOrVariableSchema).min(1),
|
|
301
|
+
instructions: z.string().trim().min(1).max(MAX_SERVER_INSTRUCTIONS).optional(),
|
|
302
|
+
capabilities: z.array(assistantCapabilitySchema).optional(),
|
|
303
|
+
sessionClaims: sessionClaimsSchema.optional(),
|
|
304
|
+
// Overrides the assistant's own opt-in for sessions minted on this surface, in either
|
|
305
|
+
// direction (ADR 0220, amended). Declared explicitly rather than inherited from
|
|
306
|
+
// `assistantUiSchema.shape` — this is not renderer presentation, and a surface that
|
|
307
|
+
// acquired it by a spread would be an accident rather than a decision.
|
|
308
|
+
webmcp: optionalStrictObject({ enabled: z.boolean().optional() }),
|
|
309
|
+
// Anonymous cross-page display continuity (ADR 0223, clauses 11-16), declared per public
|
|
310
|
+
// or mixed surface. These bounds mirror the structural ceilings the gateway clamps to at
|
|
311
|
+
// runtime; the ADR is the single source, and this package cannot import the gateway
|
|
312
|
+
// because the dependency runs the other way. Refusing rather than clamping is deliberate:
|
|
313
|
+
// the gateway narrows an operator's value silently by design, but a developer who asks
|
|
314
|
+
// for more than the ceiling has made a mistake, and silence would hide it until someone
|
|
315
|
+
// measured the live behaviour.
|
|
316
|
+
continuity: optionalStrictObject({
|
|
317
|
+
enabled: z.boolean().optional(),
|
|
318
|
+
windowSeconds: z.number().int().min(0).max(600).optional(),
|
|
319
|
+
maxRestores: z.number().int().min(0).max(10).optional(),
|
|
320
|
+
}),
|
|
321
|
+
})
|
|
322
|
+
.strict(),
|
|
323
|
+
]))
|
|
312
324
|
.min(1)
|
|
313
325
|
.optional(),
|
|
314
326
|
// Mirrored from the authenticated surface; the session exchange reads it here (ADR 0141,
|
|
@@ -323,7 +335,19 @@ const embeddedAssistantSchema = z
|
|
|
323
335
|
})
|
|
324
336
|
.strict()
|
|
325
337
|
.superRefine((assistant, ctx) => {
|
|
338
|
+
const messaging = assistant.surfaces?.filter((surface) => surface.kind === 'messaging') ?? [];
|
|
339
|
+
if (messaging.length > 1)
|
|
340
|
+
ctx.addIssue({ code: 'custom', path: ['surfaces'], message: 'duplicate whatsapp surface' });
|
|
341
|
+
if (assistant.allowedOrigins.length === 0 && messaging.length === 0) {
|
|
342
|
+
ctx.addIssue({
|
|
343
|
+
code: 'custom',
|
|
344
|
+
path: ['allowedOrigins'],
|
|
345
|
+
message: 'a website assistant requires allowedOrigins',
|
|
346
|
+
});
|
|
347
|
+
}
|
|
326
348
|
assistant.surfaces?.forEach((surface, index) => {
|
|
349
|
+
if (surface.kind === 'messaging')
|
|
350
|
+
return;
|
|
327
351
|
// The authenticated direction already reattaches through ADR 0223 clause 7, authorized by a
|
|
328
352
|
// backend-verified subject rather than by possession of a handle. A `continuity` block here
|
|
329
353
|
// would be a developer believing they configured something nothing reads, so refuse it rather
|
|
@@ -18,6 +18,7 @@ import type { CompileError } from '../errors.js';
|
|
|
18
18
|
* ADR 0055's third class — a declared anonymous fallback — stays reserved, not produced.
|
|
19
19
|
*/
|
|
20
20
|
interface AssistantSurfaceInput {
|
|
21
|
+
readonly kind?: 'website' | 'messaging' | undefined;
|
|
21
22
|
readonly mode: 'authenticated' | 'public' | 'mixed';
|
|
22
23
|
readonly capabilities?: readonly {
|
|
23
24
|
readonly kind: string;
|
|
@@ -26,12 +26,31 @@ export function validateWebsiteProjection(assistant, surfaces) {
|
|
|
26
26
|
});
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
|
+
if (surface.kind === 'messaging' && !['tool', 'knowledge'].includes(capability.kind)) {
|
|
30
|
+
errors.push({
|
|
31
|
+
code: 'assistant_messaging_unsupported',
|
|
32
|
+
path: `server.assistant.surfaces[${surfaceIndex}].capabilities[${index}]`,
|
|
33
|
+
message: 'Messaging currently supports pure read tools and knowledge only',
|
|
34
|
+
});
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
29
37
|
if (surface.mode === 'authenticated' || capability.kind !== 'tool')
|
|
30
38
|
return;
|
|
31
39
|
const tool = toolsByName.get(capability.name);
|
|
32
40
|
if (tool === undefined)
|
|
33
41
|
return;
|
|
34
42
|
const path = `tools.${tool.name}`;
|
|
43
|
+
if (surface.kind === 'messaging' &&
|
|
44
|
+
(tool.annotations?.readOnlyHint !== true ||
|
|
45
|
+
tool._meta?.ui !== undefined ||
|
|
46
|
+
tool.fulfilment.kind === 'operation' ||
|
|
47
|
+
tool.fulfilment.steps.some((step) => step.kind === 'operation'))) {
|
|
48
|
+
errors.push({
|
|
49
|
+
code: 'assistant_messaging_unsupported',
|
|
50
|
+
path,
|
|
51
|
+
message: `"${tool.name}" requires a write, connector, or UI renderer not enabled for this messaging slice`,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
35
54
|
if (surface.mode === 'public' && anonymousBehavior(tool) === 'requires-identity') {
|
|
36
55
|
errors.push({
|
|
37
56
|
code: 'assistant_public_user_reference',
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { MODULE_API_VERSION } from '@noodle-borg/module';
|
|
2
|
+
import { PostgresAuditStore } from './postgres-audit.js';
|
|
3
|
+
import { createAuditEventsRoute } from './route.js';
|
|
4
|
+
export { ensureAuditSchema, insertAuditEvent, PostgresAuditStore } from './postgres-audit.js';
|
|
5
|
+
export { createAuditEventsRoute } from './route.js';
|
|
6
|
+
export function createModule() {
|
|
7
|
+
return {
|
|
8
|
+
name: '@noodle-borg/module-audit',
|
|
9
|
+
version: '0.0.0',
|
|
10
|
+
apiVersion: MODULE_API_VERSION,
|
|
11
|
+
init: async (ctx) => {
|
|
12
|
+
const pool = ctx.stores?.postgresPool?.();
|
|
13
|
+
if (!isPgPool(pool)) {
|
|
14
|
+
throw new Error('@noodle-borg/module-audit requires a Postgres pool');
|
|
15
|
+
}
|
|
16
|
+
const store = new PostgresAuditStore(pool);
|
|
17
|
+
await store.ensureSchema();
|
|
18
|
+
return {
|
|
19
|
+
auditStore: store,
|
|
20
|
+
routes: [createAuditEventsRoute(store)],
|
|
21
|
+
readiness: async () => {
|
|
22
|
+
try {
|
|
23
|
+
await pool.query('SELECT 1');
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function isPgPool(value) {
|
|
35
|
+
return typeof value === 'object' && value !== null && typeof value.query === 'function';
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { AUDIT_SCHEMA_VERSION, redactDetails, } from '@noodle-borg/module';
|
|
3
|
+
/**
|
|
4
|
+
* Create the append-only `audit_events` table if absent (idempotent; run once at startup). Unlike
|
|
5
|
+
* `deploy_records`/`config_values`, this table has **no foreign key to `environments`** — audit evidence
|
|
6
|
+
* must survive tenant/app/env deletion, so it carries plain `org_slug`/`app_slug`/`environment` text and is
|
|
7
|
+
* never cascade-deleted. `seq` (bigserial) gives a stable newest-first order and a future cursor for the
|
|
8
|
+
* audit query API; `details` is `jsonb` holding the already-redacted scalar bag.
|
|
9
|
+
*/
|
|
10
|
+
export async function ensureAuditSchema(pool) {
|
|
11
|
+
await pool.query(`
|
|
12
|
+
CREATE TABLE IF NOT EXISTS audit_events (
|
|
13
|
+
seq bigserial PRIMARY KEY,
|
|
14
|
+
id uuid NOT NULL,
|
|
15
|
+
event_type text NOT NULL,
|
|
16
|
+
org_slug text NOT NULL,
|
|
17
|
+
app_slug text,
|
|
18
|
+
environment text,
|
|
19
|
+
deployment_id text,
|
|
20
|
+
actor_subject text,
|
|
21
|
+
actor_email text,
|
|
22
|
+
decision text,
|
|
23
|
+
status text,
|
|
24
|
+
reason_code text,
|
|
25
|
+
schema_version int NOT NULL,
|
|
26
|
+
details jsonb,
|
|
27
|
+
created_at timestamptz NOT NULL
|
|
28
|
+
)
|
|
29
|
+
`);
|
|
30
|
+
await pool.query(`
|
|
31
|
+
CREATE INDEX IF NOT EXISTS audit_events_tenant_time_idx
|
|
32
|
+
ON audit_events (org_slug, app_slug, environment, seq DESC)
|
|
33
|
+
`);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Insert one canonical, redacted audit row through either a pool or an already-open transaction client.
|
|
37
|
+
* This helper deliberately does not manage a transaction: callers using a `PoolClient` keep the audit
|
|
38
|
+
* evidence in the same commit boundary as the state change it records.
|
|
39
|
+
*/
|
|
40
|
+
export async function insertAuditEvent(queryable, input, options = {}) {
|
|
41
|
+
const details = redactDetails(input.details);
|
|
42
|
+
await queryable.query(`INSERT INTO audit_events
|
|
43
|
+
(id, event_type, org_slug, app_slug, environment, deployment_id, actor_subject, actor_email,
|
|
44
|
+
decision, status, reason_code, schema_version, details, created_at)
|
|
45
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)`, [
|
|
46
|
+
(options.id ?? randomUUID)(),
|
|
47
|
+
input.eventType,
|
|
48
|
+
input.org,
|
|
49
|
+
input.app ?? null,
|
|
50
|
+
input.env ?? null,
|
|
51
|
+
input.deploymentId ?? null,
|
|
52
|
+
input.actorSubject ?? null,
|
|
53
|
+
input.actorEmail ?? null,
|
|
54
|
+
input.decision ?? null,
|
|
55
|
+
input.status !== undefined ? String(input.status) : null,
|
|
56
|
+
input.reasonCode ?? null,
|
|
57
|
+
AUDIT_SCHEMA_VERSION,
|
|
58
|
+
details !== undefined ? JSON.stringify(details) : null,
|
|
59
|
+
(options.now ?? (() => new Date()))(),
|
|
60
|
+
]);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Relational system of record for {@link AuditEvent}s (parallels {@link InMemoryAuditStore}). The `pg.Pool`
|
|
64
|
+
* is injected; this class is pure SQL and unit-testable against any local Postgres.
|
|
65
|
+
*/
|
|
66
|
+
export class PostgresAuditStore {
|
|
67
|
+
#pool;
|
|
68
|
+
#now;
|
|
69
|
+
#id;
|
|
70
|
+
constructor(pool, options = {}) {
|
|
71
|
+
this.#pool = pool;
|
|
72
|
+
this.#now = options.now ?? (() => new Date());
|
|
73
|
+
this.#id = options.id ?? randomUUID;
|
|
74
|
+
}
|
|
75
|
+
ensureSchema() {
|
|
76
|
+
return ensureAuditSchema(this.#pool);
|
|
77
|
+
}
|
|
78
|
+
async emit(input) {
|
|
79
|
+
await insertAuditEvent(this.#pool, input, { now: this.#now, id: this.#id });
|
|
80
|
+
}
|
|
81
|
+
async list(filter) {
|
|
82
|
+
const org = validateSlug('org', filter.org);
|
|
83
|
+
const clauses = ['org_slug = $1'];
|
|
84
|
+
const values = [org];
|
|
85
|
+
if (filter.app !== undefined) {
|
|
86
|
+
values.push(filter.app);
|
|
87
|
+
clauses.push(`app_slug = $${values.length}`);
|
|
88
|
+
}
|
|
89
|
+
if (filter.env !== undefined) {
|
|
90
|
+
values.push(filter.env);
|
|
91
|
+
clauses.push(`environment = $${values.length}`);
|
|
92
|
+
}
|
|
93
|
+
if (filter.eventType !== undefined) {
|
|
94
|
+
values.push(filter.eventType);
|
|
95
|
+
clauses.push(`event_type = $${values.length}`);
|
|
96
|
+
}
|
|
97
|
+
const limit = filter.limit !== undefined ? ` LIMIT ${Number(filter.limit)}` : '';
|
|
98
|
+
const { rows } = await this.#pool.query(`SELECT * FROM audit_events WHERE ${clauses.join(' AND ')} ORDER BY seq DESC${limit}`, values);
|
|
99
|
+
return rows.map(rowToEvent);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function rowToEvent(row) {
|
|
103
|
+
return {
|
|
104
|
+
id: row.id,
|
|
105
|
+
schemaVersion: row.schema_version,
|
|
106
|
+
eventType: row.event_type,
|
|
107
|
+
org: row.org_slug,
|
|
108
|
+
createdAt: row.created_at.toISOString(),
|
|
109
|
+
...(row.app_slug !== null ? { app: row.app_slug } : {}),
|
|
110
|
+
...(row.environment !== null ? { env: row.environment } : {}),
|
|
111
|
+
...(row.deployment_id !== null ? { deploymentId: row.deployment_id } : {}),
|
|
112
|
+
...(row.actor_subject !== null ? { actorSubject: row.actor_subject } : {}),
|
|
113
|
+
...(row.actor_email !== null ? { actorEmail: row.actor_email } : {}),
|
|
114
|
+
...(row.decision !== null ? { decision: row.decision } : {}),
|
|
115
|
+
...(row.status !== null ? { status: row.status } : {}),
|
|
116
|
+
...(row.reason_code !== null ? { reasonCode: row.reason_code } : {}),
|
|
117
|
+
...(row.details !== null ? { details: row.details } : {}),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function validateSlug(label, value) {
|
|
121
|
+
if (!/^[a-z0-9][a-z0-9-]{0,62}$/.test(value)) {
|
|
122
|
+
throw new Error(`${label} must be a lowercase slug`);
|
|
123
|
+
}
|
|
124
|
+
return value;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=postgres-audit.js.map
|