@kici-dev/engine 0.0.0 → 0.1.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.
Files changed (129) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +1 -6
  3. package/dist/audit/access-log-policy.d.ts +97 -0
  4. package/dist/audit/access-log-policy.js +195 -0
  5. package/dist/audit/activity.d.ts +156 -0
  6. package/dist/audit/activity.js +175 -0
  7. package/dist/audit/retention-policy.d.ts +157 -0
  8. package/dist/audit/retention-policy.js +364 -0
  9. package/dist/bundler/index.d.ts +3 -0
  10. package/dist/bundler/index.js +3 -0
  11. package/dist/bundler/rolldown-config.d.ts +41 -0
  12. package/dist/bundler/rolldown-config.js +40 -0
  13. package/dist/chunk-gOLHoazu.js +4 -0
  14. package/dist/env/environment-allowlist.d.ts +45 -0
  15. package/dist/env/environment-allowlist.js +69 -0
  16. package/dist/environment/index.d.ts +3 -0
  17. package/dist/environment/index.js +3 -0
  18. package/dist/environment/scope-resolver.d.ts +35 -0
  19. package/dist/environment/scope-resolver.js +77 -0
  20. package/dist/environment/types.d.ts +98 -0
  21. package/dist/environment/types.js +2 -0
  22. package/dist/index.d.ts +54 -0
  23. package/dist/index.js +45 -0
  24. package/dist/labels.d.ts +106 -0
  25. package/dist/labels.js +162 -0
  26. package/dist/metrics/metric-catalog.generated.d.ts +400 -0
  27. package/dist/metrics/metric-catalog.generated.js +510 -0
  28. package/dist/protocol/dashboard-global-workflows.d.ts +9 -0
  29. package/dist/protocol/dashboard-global-workflows.js +3 -0
  30. package/dist/protocol/event-log-payload.d.ts +16 -0
  31. package/dist/protocol/event-log-payload.js +21 -0
  32. package/dist/protocol/messages/access-log.d.ts +545 -0
  33. package/dist/protocol/messages/access-log.js +175 -0
  34. package/dist/protocol/messages/actor.d.ts +100 -0
  35. package/dist/protocol/messages/actor.js +139 -0
  36. package/dist/protocol/messages/auth.d.ts +25 -0
  37. package/dist/protocol/messages/auth.js +36 -0
  38. package/dist/protocol/messages/browser.d.ts +369 -0
  39. package/dist/protocol/messages/browser.js +225 -0
  40. package/dist/protocol/messages/capabilities.d.ts +46 -0
  41. package/dist/protocol/messages/capabilities.js +42 -0
  42. package/dist/protocol/messages/common.d.ts +53 -0
  43. package/dist/protocol/messages/common.js +55 -0
  44. package/dist/protocol/messages/dashboard-global-workflows.d.ts +166 -0
  45. package/dist/protocol/messages/dashboard-global-workflows.js +80 -0
  46. package/dist/protocol/messages/dashboard.d.ts +4068 -0
  47. package/dist/protocol/messages/dashboard.js +1237 -0
  48. package/dist/protocol/messages/event-log.d.ts +49 -0
  49. package/dist/protocol/messages/event-log.js +45 -0
  50. package/dist/protocol/messages/execution-status.d.ts +164 -0
  51. package/dist/protocol/messages/execution-status.js +166 -0
  52. package/dist/protocol/messages/join.d.ts +18 -0
  53. package/dist/protocol/messages/join.js +28 -0
  54. package/dist/protocol/messages/log-pull.d.ts +27 -0
  55. package/dist/protocol/messages/log-pull.js +36 -0
  56. package/dist/protocol/messages/observe.d.ts +75 -0
  57. package/dist/protocol/messages/observe.js +70 -0
  58. package/dist/protocol/messages/orchestrator-agent.d.ts +606 -0
  59. package/dist/protocol/messages/orchestrator-agent.js +481 -0
  60. package/dist/protocol/messages/peer.d.ts +795 -0
  61. package/dist/protocol/messages/peer.js +372 -0
  62. package/dist/protocol/messages/platform-orchestrator.d.ts +1888 -0
  63. package/dist/protocol/messages/platform-orchestrator.js +379 -0
  64. package/dist/protocol/messages/run-events.d.ts +155 -0
  65. package/dist/protocol/messages/run-events.js +115 -0
  66. package/dist/protocol/messages/saas-orchestrator.d.ts +2179 -0
  67. package/dist/protocol/messages/source-registration.d.ts +103 -0
  68. package/dist/protocol/messages/source-registration.js +109 -0
  69. package/dist/protocol/messages/test-run.d.ts +59 -0
  70. package/dist/protocol/messages/test-run.js +52 -0
  71. package/dist/protocol/version.d.ts +12 -0
  72. package/dist/protocol/version.js +17 -0
  73. package/dist/provider/changed-files-fetcher.d.ts +24 -0
  74. package/dist/provider/changed-files-fetcher.js +2 -0
  75. package/dist/provider/check-run-conclusion.d.ts +15 -0
  76. package/dist/provider/check-run-conclusion.js +19 -0
  77. package/dist/provider/check-status-poster.d.ts +15 -0
  78. package/dist/provider/check-status-poster.js +2 -0
  79. package/dist/provider/clone-token-provider.d.ts +58 -0
  80. package/dist/provider/clone-token-provider.js +2 -0
  81. package/dist/provider/contributor-resolver.d.ts +22 -0
  82. package/dist/provider/contributor-resolver.js +2 -0
  83. package/dist/provider/index.d.ts +23 -0
  84. package/dist/provider/index.js +3 -0
  85. package/dist/provider/lock-file-fetcher.d.ts +24 -0
  86. package/dist/provider/lock-file-fetcher.js +2 -0
  87. package/dist/provider/repo-url-builder.d.ts +35 -0
  88. package/dist/provider/repo-url-builder.js +2 -0
  89. package/dist/provider/types.d.ts +9 -0
  90. package/dist/provider/types.js +2 -0
  91. package/dist/provider/webhook-normalizer.d.ts +163 -0
  92. package/dist/provider/webhook-normalizer.js +2 -0
  93. package/dist/registration/registerable-trigger-type.d.ts +52 -0
  94. package/dist/registration/registerable-trigger-type.js +56 -0
  95. package/dist/scaler/resource-types.d.ts +90 -0
  96. package/dist/scaler/resource-types.js +99 -0
  97. package/dist/scaler/scaler-backend-type.d.ts +16 -0
  98. package/dist/scaler/scaler-backend-type.js +20 -0
  99. package/dist/secrets/index.d.ts +7 -0
  100. package/dist/secrets/index.js +2 -0
  101. package/dist/secrets/types.d.ts +138 -0
  102. package/dist/secrets/types.js +2 -0
  103. package/dist/state-machine/index.d.ts +2 -0
  104. package/dist/state-machine/index.js +3 -0
  105. package/dist/state-machine/machine.d.ts +34 -0
  106. package/dist/state-machine/machine.js +103 -0
  107. package/dist/state-machine/types.d.ts +34 -0
  108. package/dist/state-machine/types.js +15 -0
  109. package/dist/trigger/decision-trace.d.ts +41 -0
  110. package/dist/trigger/decision-trace.js +30 -0
  111. package/dist/trigger/jsonpath-matcher.d.ts +27 -0
  112. package/dist/trigger/jsonpath-matcher.js +92 -0
  113. package/dist/trigger/matcher.d.ts +29 -0
  114. package/dist/trigger/matcher.js +704 -0
  115. package/dist/trigger/trigger-event-type.d.ts +14 -0
  116. package/dist/trigger/trigger-event-type.js +66 -0
  117. package/dist/trigger/types.d.ts +524 -0
  118. package/dist/trigger/types.js +48 -0
  119. package/dist/webhook/signature.d.ts +15 -0
  120. package/dist/webhook/signature.js +32 -0
  121. package/dist/ws/close-codes.d.ts +28 -0
  122. package/dist/ws/close-codes.js +33 -0
  123. package/dist/ws/rate-limiter.d.ts +86 -0
  124. package/dist/ws/rate-limiter.js +136 -0
  125. package/dist/ws/ws-like.d.ts +12 -0
  126. package/dist/ws/ws-like.js +2 -0
  127. package/package.json +54 -6
  128. package/sbom.spdx.json +202 -0
  129. package/index.js +0 -3
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Per-action access-log policy + sampling helper.
3
+ *
4
+ * Single source of truth for the keep / sample / rate-limit decision used by
5
+ * the orchestrator's `access_log` writer. The policy table is exhaustive
6
+ * over `AccessLogAction.options` so a new enum value without a policy entry
7
+ * is a compile-time error.
8
+ *
9
+ * Override layers (applied in `shouldRecordAccess`, in order):
10
+ *
11
+ * 1. `outcome !== 'allowed'` → always record (denied/error are full-fidelity).
12
+ * 2. `actor.type === 'platform_operator'` → always record (compliance: any
13
+ * activity by a non-org member must be auditable in full).
14
+ * 3. The per-action `POLICY_BY_ACTION` decision (`always` / `sample` /
15
+ * `rate_limit`).
16
+ *
17
+ * Browser-safe: this module MUST NOT import any Node built-ins (`node:crypto`
18
+ * etc.). It is re-exported via the `@kici-dev/engine` barrel which the
19
+ * dashboard imports. Sampling uses a tiny inline FNV-1a 32-bit hash.
20
+ */
21
+ import { z } from 'zod';
22
+ import { AccessLogAction, type AccessLogOutcome } from '../protocol/messages/access-log.js';
23
+ import { type ActorPrincipal } from '../protocol/messages/actor.js';
24
+ /** Discriminator for the per-action policy entry. */
25
+ export declare const AccessLogPolicyKind: z.ZodEnum<{
26
+ always: "always";
27
+ sample: "sample";
28
+ rate_limit: "rate_limit";
29
+ }>;
30
+ export type AccessLogPolicyKind = z.infer<typeof AccessLogPolicyKind>;
31
+ export type AccessLogPolicy = {
32
+ kind: 'always';
33
+ } | {
34
+ kind: 'sample';
35
+ allowedRate: number;
36
+ } | {
37
+ kind: 'rate_limit';
38
+ perMinutePerActor: number;
39
+ };
40
+ /**
41
+ * Minimal interface for the rate-limit decision so the engine stays
42
+ * dependency-free. Orchestrator wires in a real `SamplingRateLimiter`.
43
+ */
44
+ export interface AccessLogRateLimiter {
45
+ permit(action: AccessLogAction, actorKey: string, perMinute: number): boolean;
46
+ }
47
+ /**
48
+ * Policy table mirroring research-doc §2 verdicts (with the user's
49
+ * §7 platform-operator override applied separately in `shouldRecordAccess`).
50
+ *
51
+ * Exhaustive over `AccessLogAction.options` — TypeScript catches additions.
52
+ */
53
+ export declare const POLICY_BY_ACTION: Record<AccessLogAction, AccessLogPolicy>;
54
+ /**
55
+ * Tiny FNV-1a 32-bit hash. Used for stable hash-based sampling so a single
56
+ * actor's request stream lands in the same sampling bucket repeatedly.
57
+ *
58
+ * Pure JS, no Node built-ins (engine barrel must stay browser-safe).
59
+ */
60
+ export declare function fnv1a32(input: string): number;
61
+ /**
62
+ * Decide whether to write an `access_log` row for the given action.
63
+ *
64
+ * @param action The action being attempted.
65
+ * @param outcome `allowed` / `denied` / `error`.
66
+ * @param actor The principal performing the action.
67
+ * @param requestId The orchestrator request id (used for stable sampling).
68
+ * @param limiter Rate-limiter instance (only consulted for `rate_limit`
69
+ * actions; tests can pass a stub).
70
+ *
71
+ * Returns `true` to write the row, `false` to drop.
72
+ */
73
+ export declare function shouldRecordAccess(action: AccessLogAction, outcome: AccessLogOutcome, actor: ActorPrincipal, requestId: string | null, limiter: AccessLogRateLimiter): boolean;
74
+ /**
75
+ * Decide whether to write a `secret_audit_log` row for `resolve` /
76
+ * `resolve_named`. Volume class is per-job (every step's secret resolution),
77
+ * so the sample rate is much lower than `access_log`.
78
+ *
79
+ * The secret-resolver's `AuditEntry` doesn't carry an `ActorPrincipal` —
80
+ * we synthesise a stable key from `runId` / `jobId` so a single job's
81
+ * trace either lands wholesale in the sample or doesn't.
82
+ *
83
+ * @param entry The audit entry being written (action, outcome, runId,
84
+ * jobId, userId, role).
85
+ * @param requestId Optional sampler salt (defaults to the entry's runId
86
+ * /jobId composite); kept for symmetry with
87
+ * `shouldRecordAccess`.
88
+ */
89
+ export interface SecretResolveSampleInput {
90
+ outcome: 'allowed' | 'denied';
91
+ runId: string | null;
92
+ jobId: string | null;
93
+ userId: string | null;
94
+ role: string | null;
95
+ }
96
+ export declare function shouldRecordSecretResolve(entry: SecretResolveSampleInput): boolean;
97
+ //# sourceMappingURL=access-log-policy.d.ts.map
@@ -0,0 +1,195 @@
1
+ import "../chunk-gOLHoazu.js";
2
+ import { z } from "zod";
3
+ //#region src/audit/access-log-policy.ts
4
+ /**
5
+ * Per-action access-log policy + sampling helper.
6
+ *
7
+ * Single source of truth for the keep / sample / rate-limit decision used by
8
+ * the orchestrator's `access_log` writer. The policy table is exhaustive
9
+ * over `AccessLogAction.options` so a new enum value without a policy entry
10
+ * is a compile-time error.
11
+ *
12
+ * Override layers (applied in `shouldRecordAccess`, in order):
13
+ *
14
+ * 1. `outcome !== 'allowed'` → always record (denied/error are full-fidelity).
15
+ * 2. `actor.type === 'platform_operator'` → always record (compliance: any
16
+ * activity by a non-org member must be auditable in full).
17
+ * 3. The per-action `POLICY_BY_ACTION` decision (`always` / `sample` /
18
+ * `rate_limit`).
19
+ *
20
+ * Browser-safe: this module MUST NOT import any Node built-ins (`node:crypto`
21
+ * etc.). It is re-exported via the `@kici-dev/engine` barrel which the
22
+ * dashboard imports. Sampling uses a tiny inline FNV-1a 32-bit hash.
23
+ */
24
+ /** Discriminator for the per-action policy entry. */
25
+ const AccessLogPolicyKind = z.enum([
26
+ "always",
27
+ "sample",
28
+ "rate_limit"
29
+ ]);
30
+ /**
31
+ * Policy table mirroring research-doc §2 verdicts (with the user's
32
+ * §7 platform-operator override applied separately in `shouldRecordAccess`).
33
+ *
34
+ * Exhaustive over `AccessLogAction.options` — TypeScript catches additions.
35
+ */
36
+ const POLICY_BY_ACTION = {
37
+ "run.detail.read": {
38
+ kind: "sample",
39
+ allowedRate: .05
40
+ },
41
+ "run.orch_logs.read": {
42
+ kind: "sample",
43
+ allowedRate: .1
44
+ },
45
+ "step.logs.read": {
46
+ kind: "sample",
47
+ allowedRate: .1
48
+ },
49
+ "event_log.list.read": {
50
+ kind: "sample",
51
+ allowedRate: .05
52
+ },
53
+ "environment.list.read": {
54
+ kind: "sample",
55
+ allowedRate: .1
56
+ },
57
+ "registration.list.read": {
58
+ kind: "sample",
59
+ allowedRate: .1
60
+ },
61
+ "global_workflows.get.read": {
62
+ kind: "sample",
63
+ allowedRate: .1
64
+ },
65
+ "environment.get.read": {
66
+ kind: "sample",
67
+ allowedRate: .2
68
+ },
69
+ "environment.history.read": {
70
+ kind: "sample",
71
+ allowedRate: .2
72
+ },
73
+ "held_run.list.read": {
74
+ kind: "sample",
75
+ allowedRate: .2
76
+ },
77
+ "env_binding.list.read": {
78
+ kind: "sample",
79
+ allowedRate: .2
80
+ },
81
+ "backend.list.read": {
82
+ kind: "sample",
83
+ allowedRate: .2
84
+ },
85
+ "backend.get.read": {
86
+ kind: "sample",
87
+ allowedRate: .2
88
+ },
89
+ "diagnostics.read": {
90
+ kind: "rate_limit",
91
+ perMinutePerActor: 1
92
+ },
93
+ "scaler.capacity.read": {
94
+ kind: "rate_limit",
95
+ perMinutePerActor: 1
96
+ },
97
+ "scaler.agents.read": {
98
+ kind: "rate_limit",
99
+ perMinutePerActor: 1
100
+ },
101
+ "run.payload.read": { kind: "always" },
102
+ "run.cancel": { kind: "always" },
103
+ "run.rerun": { kind: "always" },
104
+ "run.manual_schedule": { kind: "always" },
105
+ "job.cancel": { kind: "always" },
106
+ "event_log.detail.read": { kind: "always" },
107
+ "event_log.payload.read": { kind: "always" },
108
+ "environment.create": { kind: "always" },
109
+ "environment.update": { kind: "always" },
110
+ "environment.delete": { kind: "always" },
111
+ "env_var.list.read": { kind: "always" },
112
+ "env_var.set": { kind: "always" },
113
+ "env_var.delete": { kind: "always" },
114
+ "source_override.list.read": { kind: "always" },
115
+ "source_override.set": { kind: "always" },
116
+ "source_override.delete": { kind: "always" },
117
+ "env_binding.set": { kind: "always" },
118
+ "secret.list.read": { kind: "always" },
119
+ "secret.set": { kind: "always" },
120
+ "secret.delete": { kind: "always" },
121
+ "secret.reveal": { kind: "always" },
122
+ "secret_scope.create": { kind: "always" },
123
+ "secret_scope.rename": { kind: "always" },
124
+ "secret_scope.delete": { kind: "always" },
125
+ "held_run.approve": { kind: "always" },
126
+ "held_run.reject": { kind: "always" },
127
+ "registration.disable": { kind: "always" },
128
+ "registration.delete": { kind: "always" },
129
+ "backend.sync": { kind: "always" },
130
+ "backend.sync.one": { kind: "always" },
131
+ "backend.test": { kind: "always" },
132
+ "global_workflows.update": { kind: "always" },
133
+ "access_log.list.read": { kind: "always" },
134
+ "event_dlq.list.read": {
135
+ kind: "sample",
136
+ allowedRate: .2
137
+ },
138
+ "event_dlq.retry": { kind: "always" },
139
+ "event_dlq.discard": { kind: "always" },
140
+ archive_chunk: { kind: "always" }
141
+ };
142
+ /**
143
+ * Tiny FNV-1a 32-bit hash. Used for stable hash-based sampling so a single
144
+ * actor's request stream lands in the same sampling bucket repeatedly.
145
+ *
146
+ * Pure JS, no Node built-ins (engine barrel must stay browser-safe).
147
+ */
148
+ function fnv1a32(input) {
149
+ let hash = 2166136261;
150
+ for (let i = 0; i < input.length; i++) {
151
+ hash ^= input.charCodeAt(i) & 255;
152
+ hash = Math.imul(hash, 16777619);
153
+ }
154
+ return hash >>> 0;
155
+ }
156
+ /** Build a stable cache / sampler key from the actor's flattened identity. */
157
+ function actorKey(actor) {
158
+ switch (actor.type) {
159
+ case "user": return `user:${actor.sub}`;
160
+ case "api_key": return `api_key:${actor.keyId}`;
161
+ case "service_account": return `service_account:${actor.id}`;
162
+ case "platform_operator": return `platform_operator:${actor.sub}`;
163
+ case "system": return `system:${actor.component}`;
164
+ }
165
+ }
166
+ /**
167
+ * Decide whether to write an `access_log` row for the given action.
168
+ *
169
+ * @param action The action being attempted.
170
+ * @param outcome `allowed` / `denied` / `error`.
171
+ * @param actor The principal performing the action.
172
+ * @param requestId The orchestrator request id (used for stable sampling).
173
+ * @param limiter Rate-limiter instance (only consulted for `rate_limit`
174
+ * actions; tests can pass a stub).
175
+ *
176
+ * Returns `true` to write the row, `false` to drop.
177
+ */
178
+ function shouldRecordAccess(action, outcome, actor, requestId, limiter) {
179
+ if (outcome !== "allowed") return true;
180
+ if (actor.type === "platform_operator") return true;
181
+ const policy = POLICY_BY_ACTION[action];
182
+ switch (policy.kind) {
183
+ case "always": return true;
184
+ case "sample": return fnv1a32(`${actorKey(actor)}|${requestId ?? ""}`) / 4294967296 < policy.allowedRate;
185
+ case "rate_limit": return limiter.permit(action, actorKey(actor), policy.perMinutePerActor);
186
+ }
187
+ }
188
+ function shouldRecordSecretResolve(entry) {
189
+ if (entry.outcome !== "allowed") return true;
190
+ return fnv1a32(`system:${entry.runId ?? entry.jobId ?? entry.userId ?? "unknown"}`) / 4294967296 < .01;
191
+ }
192
+ //#endregion
193
+ export { AccessLogPolicyKind, POLICY_BY_ACTION, fnv1a32, shouldRecordAccess, shouldRecordSecretResolve };
194
+
195
+ //# sourceMappingURL=access-log-policy.js.map
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Unified Activity row + filter schemas for the dashboard's federated
3
+ * "Activity" view. Federates Platform `audit_log` (mutation events,
4
+ * free-form target_type) with orchestrator `access_log` (reads + admin
5
+ * mutations, strict-enum target_type) at the query layer — see plan
6
+ * `~/.claude/plans/ui-audit-logs-page-sleepy-wave.md`.
7
+ *
8
+ * The `source` discriminator on each row tells the UI whether to render
9
+ * the audit_log or access_log column set. Fields unique to access_log
10
+ * (origin, requestId, outcome) are nullable so audit_log rows stay
11
+ * shape-compatible without padding.
12
+ *
13
+ * Browser-safe: only Zod imports + base64 helpers via globalThis.btoa/atob
14
+ * (available in Node 18+ and all evergreen browsers). No Node built-ins.
15
+ */
16
+ import { z } from 'zod';
17
+ /**
18
+ * Source discriminator on a unified Activity row.
19
+ * NOT to be confused with `AccessLogSource` (platform_proxy / admin_http /
20
+ * admin_cli) — that field is renamed to `origin` on the unified row to
21
+ * avoid the collision.
22
+ */
23
+ export declare const ActivityRowSource: z.ZodEnum<{
24
+ access_log: "access_log";
25
+ audit_log: "audit_log";
26
+ }>;
27
+ export type ActivityRowSource = z.infer<typeof ActivityRowSource>;
28
+ /**
29
+ * `source` filter parameter on /activity. `all` is the default and means
30
+ * "federate both streams"; the other values restrict to a single stream.
31
+ */
32
+ export declare const ActivityFilterSource: z.ZodEnum<{
33
+ access_log: "access_log";
34
+ audit_log: "audit_log";
35
+ all: "all";
36
+ }>;
37
+ export type ActivityFilterSource = z.infer<typeof ActivityFilterSource>;
38
+ /**
39
+ * Unified row shape returned by GET /orgs/:customerId/activity.
40
+ *
41
+ * - audit_log rows: outcome=null, errorMessage=null, requestId=undefined,
42
+ * origin=undefined. target_type/target_id are free-form strings.
43
+ * - access_log rows: outcome populated, errorMessage may be set on
44
+ * denied/error, requestId/origin populated.
45
+ */
46
+ export declare const activityRowSchema: z.ZodObject<{
47
+ id: z.ZodString;
48
+ source: z.ZodEnum<{
49
+ access_log: "access_log";
50
+ audit_log: "audit_log";
51
+ }>;
52
+ createdAt: z.ZodString;
53
+ actorType: z.ZodEnum<{
54
+ user: "user";
55
+ api_key: "api_key";
56
+ service_account: "service_account";
57
+ platform_operator: "platform_operator";
58
+ system: "system";
59
+ }>;
60
+ actorId: z.ZodString;
61
+ actorMeta: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
62
+ actorEmail: z.ZodOptional<z.ZodNullable<z.ZodString>>;
63
+ actorDisplayName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
64
+ action: z.ZodString;
65
+ targetType: z.ZodNullable<z.ZodString>;
66
+ targetId: z.ZodNullable<z.ZodString>;
67
+ outcome: z.ZodNullable<z.ZodEnum<{
68
+ error: "error";
69
+ allowed: "allowed";
70
+ denied: "denied";
71
+ }>>;
72
+ errorMessage: z.ZodNullable<z.ZodString>;
73
+ details: z.ZodNullable<z.ZodUnknown>;
74
+ requestId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
75
+ origin: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
76
+ platform_proxy: "platform_proxy";
77
+ admin_http: "admin_http";
78
+ admin_cli: "admin_cli";
79
+ }>>>;
80
+ }, z.core.$strip>;
81
+ export type ActivityRow = z.infer<typeof activityRowSchema>;
82
+ /**
83
+ * Unified filter schema accepted by GET /orgs/:customerId/activity.
84
+ * Captures every filter the existing CLI commands expose, plus the new
85
+ * search features (runId sugar, full-text q).
86
+ */
87
+ export declare const activityFilterSchema: z.ZodObject<{
88
+ source: z.ZodDefault<z.ZodEnum<{
89
+ access_log: "access_log";
90
+ audit_log: "audit_log";
91
+ all: "all";
92
+ }>>;
93
+ actorType: z.ZodOptional<z.ZodEnum<{
94
+ user: "user";
95
+ api_key: "api_key";
96
+ service_account: "service_account";
97
+ platform_operator: "platform_operator";
98
+ system: "system";
99
+ }>>;
100
+ actorId: z.ZodOptional<z.ZodString>;
101
+ action: z.ZodOptional<z.ZodString>;
102
+ outcome: z.ZodOptional<z.ZodEnum<{
103
+ error: "error";
104
+ allowed: "allowed";
105
+ denied: "denied";
106
+ }>>;
107
+ origin: z.ZodOptional<z.ZodEnum<{
108
+ platform_proxy: "platform_proxy";
109
+ admin_http: "admin_http";
110
+ admin_cli: "admin_cli";
111
+ }>>;
112
+ targetType: z.ZodOptional<z.ZodString>;
113
+ targetId: z.ZodOptional<z.ZodString>;
114
+ runId: z.ZodOptional<z.ZodString>;
115
+ q: z.ZodOptional<z.ZodString>;
116
+ from: z.ZodOptional<z.ZodString>;
117
+ to: z.ZodOptional<z.ZodString>;
118
+ cursor: z.ZodOptional<z.ZodString>;
119
+ limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
120
+ }, z.core.$strip>;
121
+ export type ActivityFilter = z.infer<typeof activityFilterSchema>;
122
+ /**
123
+ * Internal cursor shape — preserves both halves of the federated
124
+ * pagination state. `null` for either half means that source is exhausted
125
+ * and should not be re-queried on the next page.
126
+ *
127
+ * Encoded as base64url(JSON) to keep it opaque-looking and URL-safe.
128
+ */
129
+ export declare const activityCursorSchema: z.ZodObject<{
130
+ audit: z.ZodNullable<z.ZodObject<{
131
+ offset: z.ZodNumber;
132
+ }, z.core.$strip>>;
133
+ access: z.ZodNullable<z.ZodObject<{
134
+ inner: z.ZodString;
135
+ }, z.core.$strip>>;
136
+ }, z.core.$strip>;
137
+ export type ActivityCursor = z.infer<typeof activityCursorSchema>;
138
+ /**
139
+ * Encode a cursor for the federated /activity endpoint. Returns a
140
+ * URL-safe opaque string suitable for the `cursor` query param.
141
+ */
142
+ export declare function encodeActivityCursor(cursor: ActivityCursor): string;
143
+ /**
144
+ * Decode an opaque cursor string. Returns `null` when the input is
145
+ * malformed (corrupted base64, invalid JSON, or schema mismatch). Callers
146
+ * should treat null as "start from page zero" rather than 500ing.
147
+ */
148
+ export declare function decodeActivityCursor(cursor: string): ActivityCursor | null;
149
+ /**
150
+ * Apply the runId sugar: when `runId` is set on the filter, it is mapped
151
+ * to (targetType='run', targetId=runId) for both halves of the
152
+ * federation. Returns a new filter with runId resolved into the explicit
153
+ * target fields; existing target* fields take precedence if both are set.
154
+ */
155
+ export declare function resolveRunIdSugar(filter: ActivityFilter): ActivityFilter;
156
+ //# sourceMappingURL=activity.d.ts.map
@@ -0,0 +1,175 @@
1
+ import "../chunk-gOLHoazu.js";
2
+ import { ActorType } from "../protocol/messages/actor.js";
3
+ import { AccessLogOutcome } from "../protocol/messages/access-log.js";
4
+ import { z } from "zod";
5
+ //#region src/audit/activity.ts
6
+ /**
7
+ * Unified Activity row + filter schemas for the dashboard's federated
8
+ * "Activity" view. Federates Platform `audit_log` (mutation events,
9
+ * free-form target_type) with orchestrator `access_log` (reads + admin
10
+ * mutations, strict-enum target_type) at the query layer — see plan
11
+ * `~/.claude/plans/ui-audit-logs-page-sleepy-wave.md`.
12
+ *
13
+ * The `source` discriminator on each row tells the UI whether to render
14
+ * the audit_log or access_log column set. Fields unique to access_log
15
+ * (origin, requestId, outcome) are nullable so audit_log rows stay
16
+ * shape-compatible without padding.
17
+ *
18
+ * Browser-safe: only Zod imports + base64 helpers via globalThis.btoa/atob
19
+ * (available in Node 18+ and all evergreen browsers). No Node built-ins.
20
+ */
21
+ /**
22
+ * Source discriminator on a unified Activity row.
23
+ * NOT to be confused with `AccessLogSource` (platform_proxy / admin_http /
24
+ * admin_cli) — that field is renamed to `origin` on the unified row to
25
+ * avoid the collision.
26
+ */
27
+ const ActivityRowSource = z.enum(["audit_log", "access_log"]);
28
+ /**
29
+ * `source` filter parameter on /activity. `all` is the default and means
30
+ * "federate both streams"; the other values restrict to a single stream.
31
+ */
32
+ const ActivityFilterSource = z.enum([
33
+ "audit_log",
34
+ "access_log",
35
+ "all"
36
+ ]);
37
+ /**
38
+ * Unified row shape returned by GET /orgs/:customerId/activity.
39
+ *
40
+ * - audit_log rows: outcome=null, errorMessage=null, requestId=undefined,
41
+ * origin=undefined. target_type/target_id are free-form strings.
42
+ * - access_log rows: outcome populated, errorMessage may be set on
43
+ * denied/error, requestId/origin populated.
44
+ */
45
+ const activityRowSchema = z.object({
46
+ id: z.string(),
47
+ source: ActivityRowSource,
48
+ /** Merge key (createdAt DESC). ISO 8601. */
49
+ createdAt: z.string(),
50
+ /** Resolved actor identity. Display fields populated for user / platform_operator actors. */
51
+ actorType: ActorType,
52
+ actorId: z.string(),
53
+ actorMeta: z.record(z.string(), z.unknown()).nullable(),
54
+ actorEmail: z.string().nullable().optional(),
55
+ actorDisplayName: z.string().nullable().optional(),
56
+ /** Free-form on audit_log; AccessLogAction-shaped on access_log. */
57
+ action: z.string(),
58
+ /** Free-form string on audit_log (e.g. "member"); enum-bounded on access_log. */
59
+ targetType: z.string().nullable(),
60
+ targetId: z.string().nullable(),
61
+ /** access_log only; null on audit_log (audit_log has no outcome concept). */
62
+ outcome: AccessLogOutcome.nullable(),
63
+ /** access_log only; surfaces denied-reason and error text. */
64
+ errorMessage: z.string().nullable(),
65
+ /**
66
+ * audit_log: jsonb `details` column (field changes etc.).
67
+ * access_log: not populated by the federation (per-row metadata is in
68
+ * actorMeta); kept on the schema so the row shape is uniform.
69
+ */
70
+ details: z.unknown().nullable(),
71
+ /** access_log only — request correlation ID. */
72
+ requestId: z.string().nullable().optional(),
73
+ /**
74
+ * access_log only — origin of the entry (platform_proxy / admin_http /
75
+ * admin_cli). Renamed from `source` on the access_log row schema so the
76
+ * unified row's `source` field is unambiguously the audit_log vs.
77
+ * access_log discriminator.
78
+ */
79
+ origin: z.enum([
80
+ "platform_proxy",
81
+ "admin_http",
82
+ "admin_cli"
83
+ ]).nullable().optional()
84
+ });
85
+ /**
86
+ * Unified filter schema accepted by GET /orgs/:customerId/activity.
87
+ * Captures every filter the existing CLI commands expose, plus the new
88
+ * search features (runId sugar, full-text q).
89
+ */
90
+ const activityFilterSchema = z.object({
91
+ source: ActivityFilterSource.default("all"),
92
+ actorType: ActorType.optional(),
93
+ actorId: z.string().optional(),
94
+ /** Free-form: matches access_log AccessLogAction values OR audit_log free-form actions. */
95
+ action: z.string().optional(),
96
+ outcome: AccessLogOutcome.optional(),
97
+ origin: z.enum([
98
+ "platform_proxy",
99
+ "admin_http",
100
+ "admin_cli"
101
+ ]).optional(),
102
+ targetType: z.string().optional(),
103
+ targetId: z.string().optional(),
104
+ /** Sugar: maps to targetType='run' + targetId=runId. Both halves of the federation respect it. */
105
+ runId: z.string().optional(),
106
+ /** Full-text search over access_log.error_message and audit_log.details::text. */
107
+ q: z.string().optional(),
108
+ /** ISO timestamp lower bound (inclusive). */
109
+ from: z.string().optional(),
110
+ /** ISO timestamp upper bound (exclusive). */
111
+ to: z.string().optional(),
112
+ /** Opaque cursor produced by encodeActivityCursor(). */
113
+ cursor: z.string().optional(),
114
+ limit: z.coerce.number().int().min(1).max(200).default(50)
115
+ });
116
+ /**
117
+ * Internal cursor shape — preserves both halves of the federated
118
+ * pagination state. `null` for either half means that source is exhausted
119
+ * and should not be re-queried on the next page.
120
+ *
121
+ * Encoded as base64url(JSON) to keep it opaque-looking and URL-safe.
122
+ */
123
+ const activityCursorSchema = z.object({
124
+ audit: z.object({ offset: z.number().int().nonnegative() }).nullable(),
125
+ access: z.object({
126
+ /** Inner cursor returned by the orchestrator's dashboard.access-log.list response. */
127
+ inner: z.string() }).nullable()
128
+ });
129
+ function base64UrlEncode(s) {
130
+ return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
131
+ }
132
+ function base64UrlDecode(s) {
133
+ const pad = (4 - s.length % 4) % 4;
134
+ const padded = (s + "=".repeat(pad)).replace(/-/g, "+").replace(/_/g, "/");
135
+ return atob(padded);
136
+ }
137
+ /**
138
+ * Encode a cursor for the federated /activity endpoint. Returns a
139
+ * URL-safe opaque string suitable for the `cursor` query param.
140
+ */
141
+ function encodeActivityCursor(cursor) {
142
+ return base64UrlEncode(JSON.stringify(cursor));
143
+ }
144
+ /**
145
+ * Decode an opaque cursor string. Returns `null` when the input is
146
+ * malformed (corrupted base64, invalid JSON, or schema mismatch). Callers
147
+ * should treat null as "start from page zero" rather than 500ing.
148
+ */
149
+ function decodeActivityCursor(cursor) {
150
+ try {
151
+ const json = base64UrlDecode(cursor);
152
+ const parsed = activityCursorSchema.safeParse(JSON.parse(json));
153
+ return parsed.success ? parsed.data : null;
154
+ } catch {
155
+ return null;
156
+ }
157
+ }
158
+ /**
159
+ * Apply the runId sugar: when `runId` is set on the filter, it is mapped
160
+ * to (targetType='run', targetId=runId) for both halves of the
161
+ * federation. Returns a new filter with runId resolved into the explicit
162
+ * target fields; existing target* fields take precedence if both are set.
163
+ */
164
+ function resolveRunIdSugar(filter) {
165
+ if (!filter.runId) return filter;
166
+ return {
167
+ ...filter,
168
+ targetType: filter.targetType ?? "run",
169
+ targetId: filter.targetId ?? filter.runId
170
+ };
171
+ }
172
+ //#endregion
173
+ export { ActivityFilterSource, ActivityRowSource, activityCursorSchema, activityFilterSchema, activityRowSchema, decodeActivityCursor, encodeActivityCursor, resolveRunIdSugar };
174
+
175
+ //# sourceMappingURL=activity.js.map