@kbediako/codex-orchestrator 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 (150) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +238 -0
  3. package/dist/bin/codex-orchestrator.js +507 -0
  4. package/dist/orchestrator/src/agents/builder.js +16 -0
  5. package/dist/orchestrator/src/agents/index.js +4 -0
  6. package/dist/orchestrator/src/agents/planner.js +17 -0
  7. package/dist/orchestrator/src/agents/reviewer.js +13 -0
  8. package/dist/orchestrator/src/agents/tester.js +13 -0
  9. package/dist/orchestrator/src/cli/adapters/CommandBuilder.js +20 -0
  10. package/dist/orchestrator/src/cli/adapters/CommandPlanner.js +164 -0
  11. package/dist/orchestrator/src/cli/adapters/CommandReviewer.js +32 -0
  12. package/dist/orchestrator/src/cli/adapters/CommandTester.js +33 -0
  13. package/dist/orchestrator/src/cli/adapters/index.js +4 -0
  14. package/dist/orchestrator/src/cli/config/userConfig.js +28 -0
  15. package/dist/orchestrator/src/cli/doctor.js +48 -0
  16. package/dist/orchestrator/src/cli/events/runEvents.js +84 -0
  17. package/dist/orchestrator/src/cli/exec/command.js +56 -0
  18. package/dist/orchestrator/src/cli/exec/context.js +108 -0
  19. package/dist/orchestrator/src/cli/exec/experience.js +77 -0
  20. package/dist/orchestrator/src/cli/exec/finalization.js +140 -0
  21. package/dist/orchestrator/src/cli/exec/learning.js +62 -0
  22. package/dist/orchestrator/src/cli/exec/stageRunner.js +71 -0
  23. package/dist/orchestrator/src/cli/exec/summary.js +109 -0
  24. package/dist/orchestrator/src/cli/exec/telemetry.js +18 -0
  25. package/dist/orchestrator/src/cli/exec/tfgrpo.js +200 -0
  26. package/dist/orchestrator/src/cli/exec/tfgrpoArtifacts.js +19 -0
  27. package/dist/orchestrator/src/cli/exec/types.js +1 -0
  28. package/dist/orchestrator/src/cli/init.js +64 -0
  29. package/dist/orchestrator/src/cli/mcp.js +124 -0
  30. package/dist/orchestrator/src/cli/metrics/metricsAggregator.js +404 -0
  31. package/dist/orchestrator/src/cli/metrics/metricsRecorder.js +138 -0
  32. package/dist/orchestrator/src/cli/orchestrator.js +554 -0
  33. package/dist/orchestrator/src/cli/pipelines/defaultDiagnostics.js +32 -0
  34. package/dist/orchestrator/src/cli/pipelines/designReference.js +72 -0
  35. package/dist/orchestrator/src/cli/pipelines/hiFiDesignToolkit.js +71 -0
  36. package/dist/orchestrator/src/cli/pipelines/index.js +34 -0
  37. package/dist/orchestrator/src/cli/run/environment.js +24 -0
  38. package/dist/orchestrator/src/cli/run/manifest.js +367 -0
  39. package/dist/orchestrator/src/cli/run/manifestPersister.js +88 -0
  40. package/dist/orchestrator/src/cli/run/runPaths.js +30 -0
  41. package/dist/orchestrator/src/cli/selfCheck.js +12 -0
  42. package/dist/orchestrator/src/cli/services/commandRunner.js +420 -0
  43. package/dist/orchestrator/src/cli/services/controlPlaneService.js +107 -0
  44. package/dist/orchestrator/src/cli/services/execRuntime.js +69 -0
  45. package/dist/orchestrator/src/cli/services/pipelineResolver.js +47 -0
  46. package/dist/orchestrator/src/cli/services/runPreparation.js +82 -0
  47. package/dist/orchestrator/src/cli/services/runSummaryWriter.js +35 -0
  48. package/dist/orchestrator/src/cli/services/schedulerService.js +42 -0
  49. package/dist/orchestrator/src/cli/tasks/taskMetadata.js +19 -0
  50. package/dist/orchestrator/src/cli/telemetry/schema.js +8 -0
  51. package/dist/orchestrator/src/cli/types.js +1 -0
  52. package/dist/orchestrator/src/cli/ui/HudApp.js +112 -0
  53. package/dist/orchestrator/src/cli/ui/controller.js +26 -0
  54. package/dist/orchestrator/src/cli/ui/store.js +240 -0
  55. package/dist/orchestrator/src/cli/utils/enforcementMode.js +12 -0
  56. package/dist/orchestrator/src/cli/utils/fs.js +8 -0
  57. package/dist/orchestrator/src/cli/utils/interactive.js +25 -0
  58. package/dist/orchestrator/src/cli/utils/jsonlWriter.js +10 -0
  59. package/dist/orchestrator/src/cli/utils/optionalDeps.js +30 -0
  60. package/dist/orchestrator/src/cli/utils/packageInfo.js +25 -0
  61. package/dist/orchestrator/src/cli/utils/planFormatter.js +49 -0
  62. package/dist/orchestrator/src/cli/utils/runId.js +7 -0
  63. package/dist/orchestrator/src/cli/utils/specGuardRunner.js +26 -0
  64. package/dist/orchestrator/src/cli/utils/strings.js +8 -0
  65. package/dist/orchestrator/src/cli/utils/time.js +6 -0
  66. package/dist/orchestrator/src/control-plane/drift-reporter.js +109 -0
  67. package/dist/orchestrator/src/control-plane/index.js +3 -0
  68. package/dist/orchestrator/src/control-plane/request-builder.js +217 -0
  69. package/dist/orchestrator/src/control-plane/types.js +1 -0
  70. package/dist/orchestrator/src/control-plane/validator.js +50 -0
  71. package/dist/orchestrator/src/credentials/CredentialBroker.js +1 -0
  72. package/dist/orchestrator/src/events/EventBus.js +25 -0
  73. package/dist/orchestrator/src/learning/crystalizer.js +108 -0
  74. package/dist/orchestrator/src/learning/harvester.js +146 -0
  75. package/dist/orchestrator/src/learning/manifest.js +56 -0
  76. package/dist/orchestrator/src/learning/runner.js +177 -0
  77. package/dist/orchestrator/src/learning/validator.js +164 -0
  78. package/dist/orchestrator/src/logger.js +20 -0
  79. package/dist/orchestrator/src/manager.js +388 -0
  80. package/dist/orchestrator/src/persistence/ArtifactStager.js +95 -0
  81. package/dist/orchestrator/src/persistence/ExperienceStore.js +210 -0
  82. package/dist/orchestrator/src/persistence/PersistenceCoordinator.js +65 -0
  83. package/dist/orchestrator/src/persistence/RunManifestWriter.js +23 -0
  84. package/dist/orchestrator/src/persistence/TaskStateStore.js +172 -0
  85. package/dist/orchestrator/src/persistence/identifierGuards.js +1 -0
  86. package/dist/orchestrator/src/persistence/lockFile.js +26 -0
  87. package/dist/orchestrator/src/persistence/sanitizeIdentifier.js +26 -0
  88. package/dist/orchestrator/src/persistence/sanitizeRunId.js +8 -0
  89. package/dist/orchestrator/src/persistence/sanitizeTaskId.js +8 -0
  90. package/dist/orchestrator/src/persistence/writeAtomicFile.js +4 -0
  91. package/dist/orchestrator/src/privacy/guard.js +111 -0
  92. package/dist/orchestrator/src/scheduler/index.js +1 -0
  93. package/dist/orchestrator/src/scheduler/plan.js +171 -0
  94. package/dist/orchestrator/src/scheduler/types.js +1 -0
  95. package/dist/orchestrator/src/sync/CloudRunsClient.js +1 -0
  96. package/dist/orchestrator/src/sync/CloudRunsHttpClient.js +82 -0
  97. package/dist/orchestrator/src/sync/CloudSyncWorker.js +206 -0
  98. package/dist/orchestrator/src/sync/createCloudSyncWorker.js +15 -0
  99. package/dist/orchestrator/src/types.js +1 -0
  100. package/dist/orchestrator/src/utils/atomicWrite.js +15 -0
  101. package/dist/orchestrator/src/utils/errorMessage.js +14 -0
  102. package/dist/orchestrator/src/utils/executionMode.js +69 -0
  103. package/dist/packages/control-plane-schemas/src/index.js +1 -0
  104. package/dist/packages/control-plane-schemas/src/run-request.js +548 -0
  105. package/dist/packages/orchestrator/src/exec/handle-service.js +203 -0
  106. package/dist/packages/orchestrator/src/exec/session-manager.js +147 -0
  107. package/dist/packages/orchestrator/src/exec/unified-exec.js +432 -0
  108. package/dist/packages/orchestrator/src/index.js +3 -0
  109. package/dist/packages/orchestrator/src/instructions/loader.js +101 -0
  110. package/dist/packages/orchestrator/src/instructions/promptPacks.js +151 -0
  111. package/dist/packages/orchestrator/src/notifications/index.js +74 -0
  112. package/dist/packages/orchestrator/src/telemetry/otel-exporter.js +142 -0
  113. package/dist/packages/orchestrator/src/tool-orchestrator.js +161 -0
  114. package/dist/packages/sdk-node/src/orchestrator.js +195 -0
  115. package/dist/packages/shared/config/designConfig.js +495 -0
  116. package/dist/packages/shared/config/env.js +37 -0
  117. package/dist/packages/shared/config/index.js +2 -0
  118. package/dist/packages/shared/design-artifacts/writer.js +221 -0
  119. package/dist/packages/shared/events/serializer.js +84 -0
  120. package/dist/packages/shared/events/types.js +1 -0
  121. package/dist/packages/shared/manifest/artifactUtils.js +36 -0
  122. package/dist/packages/shared/manifest/designArtifacts.js +665 -0
  123. package/dist/packages/shared/manifest/fileIO.js +29 -0
  124. package/dist/packages/shared/manifest/toolRuns.js +78 -0
  125. package/dist/packages/shared/manifest/toolkitArtifacts.js +223 -0
  126. package/dist/packages/shared/manifest/types.js +5 -0
  127. package/dist/packages/shared/manifest/validator.js +73 -0
  128. package/dist/packages/shared/manifest/writer.js +2 -0
  129. package/dist/packages/shared/streams/stdio.js +112 -0
  130. package/dist/scripts/design/pipeline/advanced-assets.js +466 -0
  131. package/dist/scripts/design/pipeline/componentize.js +74 -0
  132. package/dist/scripts/design/pipeline/context.js +34 -0
  133. package/dist/scripts/design/pipeline/extract.js +249 -0
  134. package/dist/scripts/design/pipeline/optionalDeps.js +107 -0
  135. package/dist/scripts/design/pipeline/prepare.js +46 -0
  136. package/dist/scripts/design/pipeline/reference.js +94 -0
  137. package/dist/scripts/design/pipeline/state.js +206 -0
  138. package/dist/scripts/design/pipeline/toolkit/common.js +94 -0
  139. package/dist/scripts/design/pipeline/toolkit/extract.js +258 -0
  140. package/dist/scripts/design/pipeline/toolkit/publish.js +202 -0
  141. package/dist/scripts/design/pipeline/toolkit/publishActions.js +12 -0
  142. package/dist/scripts/design/pipeline/toolkit/reference.js +846 -0
  143. package/dist/scripts/design/pipeline/toolkit/snapshot.js +882 -0
  144. package/dist/scripts/design/pipeline/toolkit/tokens.js +456 -0
  145. package/dist/scripts/design/pipeline/visual-regression.js +137 -0
  146. package/dist/scripts/design/pipeline/write-artifacts.js +61 -0
  147. package/package.json +97 -0
  148. package/schemas/manifest.json +1064 -0
  149. package/templates/README.md +12 -0
  150. package/templates/codex/mcp-client.json +8 -0
@@ -0,0 +1,221 @@
1
+ import { mkdir, rename, writeFile } from 'node:fs/promises';
2
+ import { dirname, join, relative as relativePath } from 'node:path';
3
+ import { persistDesignManifest } from '../manifest/writer.js';
4
+ export async function writeDesignSummary(options) {
5
+ const now = options.now ?? new Date();
6
+ const context = options.context;
7
+ const artifacts = options.artifacts ?? [];
8
+ const summary = options.summary ?? deriveSummary(artifacts, now);
9
+ const toolkitArtifacts = options.toolkitArtifacts ?? [];
10
+ const approvals = options.approvals ?? [];
11
+ const toolkitSummary = options.toolkitSummary ?? (toolkitArtifacts.length > 0 ? deriveToolkitSummary(toolkitArtifacts, approvals, now) : undefined);
12
+ const manifestUpdate = {
13
+ artifacts,
14
+ summary,
15
+ configSnapshot: options.configSnapshot ?? null,
16
+ ...(toolkitArtifacts.length > 0 ? { toolkitArtifacts } : {}),
17
+ ...(toolkitSummary ? { toolkitSummary } : {}),
18
+ ...(options.designPlan !== undefined ? { designPlan: options.designPlan } : {}),
19
+ ...(options.designGuardrail !== undefined ? { designGuardrail: options.designGuardrail } : {}),
20
+ ...(options.designHistory !== undefined ? { designHistory: options.designHistory } : {}),
21
+ ...(options.designStyleProfile !== undefined ? { designStyleProfile: options.designStyleProfile } : {}),
22
+ ...(options.designMetrics !== undefined ? { designMetrics: options.designMetrics } : {})
23
+ };
24
+ const manifestOptions = {
25
+ retentionDays: options.retention.days,
26
+ retentionPolicy: options.retention.policy,
27
+ now,
28
+ ...options.manifestOptions
29
+ };
30
+ const manifest = await persistDesignManifest(context.manifestPath, manifestUpdate, manifestOptions);
31
+ const payload = buildSummaryPayload(options, summary, toolkitSummary, manifest, now);
32
+ const summaryPath = await writeSummaryFile(options, payload);
33
+ return {
34
+ manifest: context.manifestPath,
35
+ summaryPath,
36
+ summary: payload
37
+ };
38
+ }
39
+ function buildSummaryPayload(options, summary, toolkitSummary, manifest, now) {
40
+ const context = options.context;
41
+ const repoRoot = options.context.repoRoot ?? process.cwd();
42
+ const manifestRelative = relativePath(repoRoot, context.manifestPath);
43
+ const approvals = (options.approvals ?? [])
44
+ .map((approval) => ({
45
+ id: approval.id,
46
+ actor: approval.actor,
47
+ reason: approval.reason,
48
+ timestamp: approval.timestamp
49
+ }))
50
+ .filter((approval) => approval.id && approval.actor && approval.timestamp);
51
+ const stages = options.stages.map((stage) => {
52
+ const artifacts = (stage.artifacts ?? []).map((artifact) => ({
53
+ relative_path: artifact.relative_path,
54
+ stage: artifact.stage ?? stage.id,
55
+ status: artifact.status ?? stage.status,
56
+ type: artifact.type,
57
+ description: artifact.description
58
+ }));
59
+ return {
60
+ id: stage.id,
61
+ title: stage.title ?? null,
62
+ status: stage.status,
63
+ notes: stage.notes ?? [],
64
+ metrics: stage.metrics ?? {},
65
+ artifacts
66
+ };
67
+ });
68
+ const summaryArtifacts = (options.artifacts ?? []).map((artifact) => ({
69
+ stage: artifact.stage,
70
+ status: artifact.status,
71
+ relative_path: artifact.relative_path,
72
+ type: artifact.type,
73
+ description: artifact.description
74
+ }));
75
+ const toolkitArtifacts = (options.toolkitArtifacts ?? []).map((artifact) => ({
76
+ id: artifact.id,
77
+ stage: artifact.stage,
78
+ status: artifact.status,
79
+ relative_path: artifact.relative_path,
80
+ description: artifact.description ?? null,
81
+ metrics: artifact.metrics ?? {},
82
+ retention: artifact.retention ?? null
83
+ }));
84
+ const designMetrics = manifest.design_metrics ?? options.designMetrics ?? null;
85
+ const combinedMetrics = {
86
+ ...(options.metrics ?? {}),
87
+ ...(designMetrics ?? {})
88
+ };
89
+ return {
90
+ task_id: context.taskId,
91
+ run_id: context.runId,
92
+ manifest: manifestRelative,
93
+ generated_at: now.toISOString(),
94
+ retention: {
95
+ days: options.retention.days,
96
+ auto_purge: options.retention.autoPurge,
97
+ policy: options.retention.policy ?? 'design.config.retention'
98
+ },
99
+ privacy: {
100
+ allow_third_party: options.privacy.allowThirdParty,
101
+ require_approval: options.privacy.requireApproval,
102
+ mask_selectors: options.privacy.maskSelectors,
103
+ approver: options.privacy.approver ?? null
104
+ },
105
+ approvals,
106
+ stages,
107
+ artifacts: summaryArtifacts,
108
+ summary,
109
+ design_toolkit_artifacts: toolkitArtifacts,
110
+ design_toolkit_summary: toolkitSummary ?? null,
111
+ design_plan: manifest.design_plan ?? options.designPlan ?? null,
112
+ design_guardrail: manifest.design_guardrail ?? options.designGuardrail ?? null,
113
+ design_history: manifest.design_history ?? options.designHistory ?? null,
114
+ design_style_profile: manifest.design_style_profile ?? options.designStyleProfile ?? null,
115
+ design_metrics: designMetrics,
116
+ metrics: combinedMetrics,
117
+ config_snapshot: options.configSnapshot ?? null
118
+ };
119
+ }
120
+ async function writeSummaryFile(options, payload) {
121
+ const outRoot = options.outDir ?? join(process.cwd(), 'out');
122
+ const safeTaskId = sanitizeTaskId(options.context.taskId);
123
+ const safeRunId = sanitizeRunId(options.context.runId);
124
+ const summaryPath = join(outRoot, safeTaskId, 'design', 'runs', `${safeRunId}.json`);
125
+ await writeJsonAtomic(summaryPath, payload);
126
+ return summaryPath;
127
+ }
128
+ function deriveSummary(artifacts, now) {
129
+ const stageMap = new Map();
130
+ for (const artifact of artifacts) {
131
+ const entry = stageMap.get(artifact.stage) ?? {
132
+ succeeded: 0,
133
+ failed: 0,
134
+ skipped: 0,
135
+ artifacts: 0
136
+ };
137
+ entry.artifacts += 1;
138
+ if (artifact.status === 'succeeded') {
139
+ entry.succeeded += 1;
140
+ }
141
+ else if (artifact.status === 'failed') {
142
+ entry.failed += 1;
143
+ }
144
+ else {
145
+ entry.skipped += 1;
146
+ }
147
+ stageMap.set(artifact.stage, entry);
148
+ }
149
+ const stages = Array.from(stageMap.entries()).map(([stage, stats]) => ({
150
+ stage,
151
+ succeeded: stats.succeeded,
152
+ failed: stats.failed,
153
+ skipped: stats.skipped,
154
+ artifacts: stats.artifacts
155
+ }));
156
+ return {
157
+ total_artifacts: artifacts.length,
158
+ generated_at: now.toISOString(),
159
+ stages
160
+ };
161
+ }
162
+ function deriveToolkitSummary(artifacts, approvals, now) {
163
+ const stageMap = new Map();
164
+ const totals = {};
165
+ for (const artifact of artifacts) {
166
+ const entry = stageMap.get(artifact.stage) ?? { count: 0, metrics: {} };
167
+ entry.count += 1;
168
+ if (artifact.metrics) {
169
+ accumulateToolkitMetrics(entry.metrics, artifact.metrics);
170
+ accumulateNumericTotals(totals, artifact.metrics);
171
+ }
172
+ stageMap.set(artifact.stage, entry);
173
+ }
174
+ const stages = Array.from(stageMap.entries()).map(([stage, data]) => ({
175
+ stage,
176
+ artifacts: data.count,
177
+ metrics: data.metrics
178
+ }));
179
+ const approvalIds = approvals.map((approval) => approval.id).filter(Boolean);
180
+ return {
181
+ generated_at: now.toISOString(),
182
+ stages,
183
+ totals: Object.keys(totals).length > 0 ? totals : undefined,
184
+ approvals: approvalIds.length > 0 ? approvalIds : undefined
185
+ };
186
+ }
187
+ function accumulateToolkitMetrics(target, incoming) {
188
+ for (const [key, value] of Object.entries(incoming)) {
189
+ if (typeof value === 'number' && Number.isFinite(value)) {
190
+ const current = target[key];
191
+ if (typeof current === 'number') {
192
+ target[key] = current + value;
193
+ }
194
+ else {
195
+ target[key] = value;
196
+ }
197
+ }
198
+ else if (typeof value === 'string') {
199
+ target[key] = value;
200
+ }
201
+ }
202
+ }
203
+ function accumulateNumericTotals(target, incoming) {
204
+ for (const [key, value] of Object.entries(incoming)) {
205
+ if (typeof value === 'number' && Number.isFinite(value)) {
206
+ target[key] = (target[key] ?? 0) + value;
207
+ }
208
+ }
209
+ }
210
+ function sanitizeTaskId(value) {
211
+ return value.replace(/[^a-zA-Z0-9_-]/g, '').toLowerCase();
212
+ }
213
+ function sanitizeRunId(value) {
214
+ return value.replace(/[:]/g, '-');
215
+ }
216
+ async function writeJsonAtomic(targetPath, payload) {
217
+ const tmpPath = `${targetPath}.tmp-${process.pid}-${Date.now()}`;
218
+ await mkdir(dirname(targetPath), { recursive: true });
219
+ await writeFile(tmpPath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
220
+ await rename(tmpPath, targetPath);
221
+ }
@@ -0,0 +1,84 @@
1
+ function serializeBegin(event) {
2
+ return {
3
+ type: event.type,
4
+ timestamp: event.timestamp,
5
+ payload: {
6
+ attempt: event.attempt,
7
+ correlationId: event.correlationId,
8
+ command: event.payload.command,
9
+ args: event.payload.args,
10
+ cwd: event.payload.cwd ?? null,
11
+ sessionId: event.payload.sessionId,
12
+ sandboxState: event.payload.sandboxState,
13
+ persisted: event.payload.persisted
14
+ }
15
+ };
16
+ }
17
+ function serializeChunk(event) {
18
+ return {
19
+ type: event.type,
20
+ timestamp: event.timestamp,
21
+ payload: {
22
+ attempt: event.attempt,
23
+ correlationId: event.correlationId,
24
+ stream: event.payload.stream,
25
+ sequence: event.payload.sequence,
26
+ bytes: event.payload.bytes,
27
+ data: event.payload.data
28
+ }
29
+ };
30
+ }
31
+ function serializeEnd(event) {
32
+ return {
33
+ type: event.type,
34
+ timestamp: event.timestamp,
35
+ payload: {
36
+ attempt: event.attempt,
37
+ correlationId: event.correlationId,
38
+ exitCode: event.payload.exitCode,
39
+ signal: event.payload.signal,
40
+ durationMs: event.payload.durationMs,
41
+ stdout: event.payload.stdout,
42
+ stderr: event.payload.stderr,
43
+ sandboxState: event.payload.sandboxState,
44
+ sessionId: event.payload.sessionId,
45
+ status: event.payload.status
46
+ }
47
+ };
48
+ }
49
+ function serializeRetry(event) {
50
+ return {
51
+ type: event.type,
52
+ timestamp: event.timestamp,
53
+ payload: {
54
+ attempt: event.attempt,
55
+ correlationId: event.correlationId,
56
+ delayMs: event.payload.delayMs,
57
+ sandboxState: event.payload.sandboxState,
58
+ errorMessage: event.payload.errorMessage
59
+ }
60
+ };
61
+ }
62
+ export function serializeExecEvent(event) {
63
+ switch (event.type) {
64
+ case 'exec:begin':
65
+ return serializeBegin(event);
66
+ case 'exec:chunk':
67
+ return serializeChunk(event);
68
+ case 'exec:end':
69
+ return serializeEnd(event);
70
+ case 'exec:retry':
71
+ return serializeRetry(event);
72
+ default: {
73
+ const exhaustive = event;
74
+ return exhaustive;
75
+ }
76
+ }
77
+ }
78
+ export function serializeRunSummaryEvent(payload, timestamp = new Date().toISOString()) {
79
+ return {
80
+ type: 'run:summary',
81
+ timestamp,
82
+ payload
83
+ };
84
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,36 @@
1
+ export function sanitizeRelativeArtifactPath(value) {
2
+ const normalized = value.replace(/\\+/g, '/').split('/');
3
+ const segments = [];
4
+ for (const segmentRaw of normalized) {
5
+ const segment = segmentRaw.trim();
6
+ if (!segment || segment === '.') {
7
+ continue;
8
+ }
9
+ if (segment === '..' || segment.includes('..')) {
10
+ throw new Error(`relative_path contains invalid segment '${segment}'`);
11
+ }
12
+ segments.push(segment);
13
+ }
14
+ if (segments.length === 0) {
15
+ throw new Error('relative_path must include at least one segment');
16
+ }
17
+ return segments.join('/');
18
+ }
19
+ export function isIsoDate(value) {
20
+ const trimmed = value.trim();
21
+ const isoPattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
22
+ if (!isoPattern.test(trimmed)) {
23
+ return false;
24
+ }
25
+ const date = new Date(trimmed);
26
+ return !Number.isNaN(date.getTime());
27
+ }
28
+ export function coerceNonNegativeInteger(value) {
29
+ if (typeof value === 'number' || typeof value === 'string') {
30
+ const converted = typeof value === 'number' ? value : Number(value);
31
+ if (Number.isFinite(converted) && converted >= 0) {
32
+ return Math.floor(converted);
33
+ }
34
+ }
35
+ return null;
36
+ }