@bastani/atomic 0.9.0-alpha.1 → 0.9.0-alpha.2

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 (211) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/builtin/cursor/CHANGELOG.md +6 -0
  3. package/dist/builtin/cursor/package.json +2 -2
  4. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  5. package/dist/builtin/intercom/package.json +2 -2
  6. package/dist/builtin/mcp/CHANGELOG.md +6 -0
  7. package/dist/builtin/mcp/package.json +3 -3
  8. package/dist/builtin/subagents/CHANGELOG.md +6 -0
  9. package/dist/builtin/subagents/package.json +4 -4
  10. package/dist/builtin/web-access/CHANGELOG.md +6 -0
  11. package/dist/builtin/web-access/package.json +2 -2
  12. package/dist/builtin/workflows/CHANGELOG.md +12 -0
  13. package/dist/builtin/workflows/README.md +189 -122
  14. package/dist/builtin/workflows/builtin/deep-research-codebase.ts +30 -27
  15. package/dist/builtin/workflows/builtin/goal-runner.ts +10 -17
  16. package/dist/builtin/workflows/builtin/goal.ts +39 -44
  17. package/dist/builtin/workflows/builtin/index.d.ts +1 -0
  18. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +16 -17
  19. package/dist/builtin/workflows/builtin/open-claude-design.d.ts +1 -0
  20. package/dist/builtin/workflows/builtin/open-claude-design.ts +42 -50
  21. package/dist/builtin/workflows/builtin/ralph.ts +44 -41
  22. package/dist/builtin/workflows/package.json +2 -2
  23. package/dist/builtin/workflows/src/authoring/typebox-defaults.d.ts +41 -0
  24. package/dist/builtin/workflows/src/authoring/typebox-defaults.ts +217 -0
  25. package/dist/builtin/workflows/src/authoring/workflow.ts +184 -0
  26. package/dist/builtin/workflows/src/authoring.d.ts +14 -66
  27. package/dist/builtin/workflows/src/engine/graph-inference.ts +100 -0
  28. package/dist/builtin/workflows/src/engine/options.ts +40 -0
  29. package/dist/builtin/workflows/src/engine/primitives/chain.ts +29 -0
  30. package/dist/builtin/workflows/src/engine/primitives/exit.ts +2 -0
  31. package/dist/builtin/workflows/src/engine/primitives/parallel.ts +47 -0
  32. package/dist/builtin/workflows/src/engine/primitives/task.ts +108 -0
  33. package/dist/builtin/workflows/src/engine/primitives/ui.ts +41 -0
  34. package/dist/builtin/workflows/src/engine/primitives/workflow.ts +159 -0
  35. package/dist/builtin/workflows/src/engine/replay.ts +8 -0
  36. package/dist/builtin/workflows/src/engine/run.ts +356 -0
  37. package/dist/builtin/workflows/src/engine/runtime.ts +160 -0
  38. package/dist/builtin/workflows/src/extension/workflow-module-loader.ts +9 -3
  39. package/dist/builtin/workflows/src/extension/workflow-schema.ts +0 -18
  40. package/dist/builtin/workflows/src/index.ts +0 -2
  41. package/dist/builtin/workflows/src/runs/background/runner.ts +6 -3
  42. package/dist/builtin/workflows/src/runs/foreground/executor-child-boundary.ts +3 -3
  43. package/dist/builtin/workflows/src/runs/foreground/executor-child-helpers.ts +4 -4
  44. package/dist/builtin/workflows/src/runs/foreground/executor-child-workflow.ts +1 -158
  45. package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +1 -1
  46. package/dist/builtin/workflows/src/runs/foreground/executor-outputs.ts +2 -2
  47. package/dist/builtin/workflows/src/runs/foreground/executor-prompt-nodes.ts +1 -1
  48. package/dist/builtin/workflows/src/runs/foreground/executor-run.ts +1 -359
  49. package/dist/builtin/workflows/src/runs/foreground/executor-scheduler.ts +1 -1
  50. package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +2 -5
  51. package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +12 -4
  52. package/dist/builtin/workflows/src/runs/foreground/executor-stage-replay.ts +4 -3
  53. package/dist/builtin/workflows/src/runs/foreground/executor-stage-types.ts +9 -2
  54. package/dist/builtin/workflows/src/runs/foreground/executor-task-context.ts +2 -132
  55. package/dist/builtin/workflows/src/runs/foreground/executor-types.ts +2 -2
  56. package/dist/builtin/workflows/src/runs/shared/graph-inference.ts +2 -100
  57. package/dist/builtin/workflows/src/sdk-surface.ts +6 -9
  58. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +9 -3
  59. package/dist/builtin/workflows/src/shared/authoring-contract-stage.ts +17 -3
  60. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +3 -33
  61. package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +9 -81
  62. package/dist/builtin/workflows/src/shared/types.ts +25 -8
  63. package/dist/builtin/workflows/src/shared/workflow-authoring-types.d.ts +49 -0
  64. package/dist/builtin/workflows/src/shared/workflow-authoring-types.ts +84 -0
  65. package/dist/builtin/workflows/src/workflows/registry.ts +7 -3
  66. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  67. package/dist/core/agent-session-auto-compaction.js +6 -1
  68. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  69. package/dist/core/agent-session-bash.d.ts.map +1 -1
  70. package/dist/core/agent-session-bash.js +0 -5
  71. package/dist/core/agent-session-bash.js.map +1 -1
  72. package/dist/core/agent-session-methods.d.ts +0 -2
  73. package/dist/core/agent-session-methods.d.ts.map +1 -1
  74. package/dist/core/agent-session-methods.js.map +1 -1
  75. package/dist/core/agent-session-services.d.ts +0 -1
  76. package/dist/core/agent-session-services.d.ts.map +1 -1
  77. package/dist/core/agent-session-services.js +0 -1
  78. package/dist/core/agent-session-services.js.map +1 -1
  79. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  80. package/dist/core/agent-session-tool-registry.js +0 -2
  81. package/dist/core/agent-session-tool-registry.js.map +1 -1
  82. package/dist/core/agent-session-types.d.ts +0 -2
  83. package/dist/core/agent-session-types.d.ts.map +1 -1
  84. package/dist/core/agent-session-types.js.map +1 -1
  85. package/dist/core/agent-session.d.ts +0 -2
  86. package/dist/core/agent-session.d.ts.map +1 -1
  87. package/dist/core/agent-session.js +0 -1
  88. package/dist/core/agent-session.js.map +1 -1
  89. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  90. package/dist/core/atomic-guide-command.js +1 -1
  91. package/dist/core/atomic-guide-command.js.map +1 -1
  92. package/dist/core/extensions/loader-core.d.ts +1 -3
  93. package/dist/core/extensions/loader-core.d.ts.map +1 -1
  94. package/dist/core/extensions/loader-core.js +13 -6
  95. package/dist/core/extensions/loader-core.js.map +1 -1
  96. package/dist/core/extensions/loader-virtual-modules.d.ts +7 -1
  97. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  98. package/dist/core/extensions/loader-virtual-modules.js +34 -2
  99. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  100. package/dist/core/extensions/loader.d.ts +2 -1
  101. package/dist/core/extensions/loader.d.ts.map +1 -1
  102. package/dist/core/extensions/loader.js +2 -1
  103. package/dist/core/extensions/loader.js.map +1 -1
  104. package/dist/core/index.d.ts +0 -1
  105. package/dist/core/index.d.ts.map +1 -1
  106. package/dist/core/index.js +0 -1
  107. package/dist/core/index.js.map +1 -1
  108. package/dist/core/model-registry-builtins.d.ts.map +1 -1
  109. package/dist/core/model-registry-builtins.js +6 -0
  110. package/dist/core/model-registry-builtins.js.map +1 -1
  111. package/dist/core/model-registry-schemas.d.ts +65 -13
  112. package/dist/core/model-registry-schemas.d.ts.map +1 -1
  113. package/dist/core/model-registry-schemas.js +10 -0
  114. package/dist/core/model-registry-schemas.js.map +1 -1
  115. package/dist/core/resource-loader-core.d.ts +1 -0
  116. package/dist/core/resource-loader-core.d.ts.map +1 -1
  117. package/dist/core/resource-loader-core.js +2 -0
  118. package/dist/core/resource-loader-core.js.map +1 -1
  119. package/dist/core/resource-loader-extensions.d.ts.map +1 -1
  120. package/dist/core/resource-loader-extensions.js +3 -3
  121. package/dist/core/resource-loader-extensions.js.map +1 -1
  122. package/dist/core/resource-loader-internals.d.ts +1 -0
  123. package/dist/core/resource-loader-internals.d.ts.map +1 -1
  124. package/dist/core/resource-loader-internals.js.map +1 -1
  125. package/dist/core/resource-loader-reload.d.ts.map +1 -1
  126. package/dist/core/resource-loader-reload.js +6 -2
  127. package/dist/core/resource-loader-reload.js.map +1 -1
  128. package/dist/core/sdk-exports.d.ts +1 -1
  129. package/dist/core/sdk-exports.d.ts.map +1 -1
  130. package/dist/core/sdk-exports.js.map +1 -1
  131. package/dist/core/sdk-types.d.ts +0 -3
  132. package/dist/core/sdk-types.d.ts.map +1 -1
  133. package/dist/core/sdk-types.js.map +1 -1
  134. package/dist/core/sdk.d.ts.map +1 -1
  135. package/dist/core/sdk.js +0 -1
  136. package/dist/core/sdk.js.map +1 -1
  137. package/dist/core/session-manager-history.d.ts.map +1 -1
  138. package/dist/core/session-manager-history.js +2 -1
  139. package/dist/core/session-manager-history.js.map +1 -1
  140. package/dist/core/tools/bash.d.ts +0 -5
  141. package/dist/core/tools/bash.d.ts.map +1 -1
  142. package/dist/core/tools/bash.js +10 -11
  143. package/dist/core/tools/bash.js.map +1 -1
  144. package/dist/core/tools/edit-diff-preserve.d.ts +18 -0
  145. package/dist/core/tools/edit-diff-preserve.d.ts.map +1 -0
  146. package/dist/core/tools/edit-diff-preserve.js +85 -0
  147. package/dist/core/tools/edit-diff-preserve.js.map +1 -0
  148. package/dist/core/tools/edit-diff.d.ts +3 -2
  149. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  150. package/dist/core/tools/edit-diff.js +15 -18
  151. package/dist/core/tools/edit-diff.js.map +1 -1
  152. package/dist/core/tools/index.d.ts +0 -1
  153. package/dist/core/tools/index.d.ts.map +1 -1
  154. package/dist/core/tools/index.js +0 -1
  155. package/dist/core/tools/index.js.map +1 -1
  156. package/dist/index.d.ts +2 -2
  157. package/dist/index.d.ts.map +1 -1
  158. package/dist/index.js +1 -1
  159. package/dist/index.js.map +1 -1
  160. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  161. package/dist/modes/interactive/components/model-selector.js +2 -2
  162. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  163. package/dist/modes/interactive/model-search.d.ts +5 -0
  164. package/dist/modes/interactive/model-search.d.ts.map +1 -1
  165. package/dist/modes/interactive/model-search.js +9 -0
  166. package/dist/modes/interactive/model-search.js.map +1 -1
  167. package/dist/utils/shell.d.ts +1 -0
  168. package/dist/utils/shell.d.ts.map +1 -1
  169. package/dist/utils/shell.js +12 -5
  170. package/dist/utils/shell.js.map +1 -1
  171. package/docs/custom-provider.md +4 -3
  172. package/docs/models.md +3 -2
  173. package/docs/packages.md +2 -2
  174. package/docs/quickstart.md +1 -1
  175. package/docs/sdk.md +2 -40
  176. package/docs/security.md +1 -1
  177. package/docs/workflows.md +238 -173
  178. package/package.json +5 -5
  179. package/dist/builtin/workflows/src/workflows/define-workflow.ts +0 -277
  180. package/dist/core/tools/bash-policy-compile.d.ts +0 -5
  181. package/dist/core/tools/bash-policy-compile.d.ts.map +0 -1
  182. package/dist/core/tools/bash-policy-compile.js +0 -241
  183. package/dist/core/tools/bash-policy-compile.js.map +0 -1
  184. package/dist/core/tools/bash-policy-evaluate.d.ts +0 -3
  185. package/dist/core/tools/bash-policy-evaluate.d.ts.map +0 -1
  186. package/dist/core/tools/bash-policy-evaluate.js +0 -92
  187. package/dist/core/tools/bash-policy-evaluate.js.map +0 -1
  188. package/dist/core/tools/bash-policy-format.d.ts +0 -5
  189. package/dist/core/tools/bash-policy-format.d.ts.map +0 -1
  190. package/dist/core/tools/bash-policy-format.js +0 -49
  191. package/dist/core/tools/bash-policy-format.js.map +0 -1
  192. package/dist/core/tools/bash-policy-parser.d.ts +0 -4
  193. package/dist/core/tools/bash-policy-parser.d.ts.map +0 -1
  194. package/dist/core/tools/bash-policy-parser.js +0 -155
  195. package/dist/core/tools/bash-policy-parser.js.map +0 -1
  196. package/dist/core/tools/bash-policy-segment.d.ts +0 -3
  197. package/dist/core/tools/bash-policy-segment.d.ts.map +0 -1
  198. package/dist/core/tools/bash-policy-segment.js +0 -275
  199. package/dist/core/tools/bash-policy-segment.js.map +0 -1
  200. package/dist/core/tools/bash-policy-shell.d.ts +0 -11
  201. package/dist/core/tools/bash-policy-shell.d.ts.map +0 -1
  202. package/dist/core/tools/bash-policy-shell.js +0 -267
  203. package/dist/core/tools/bash-policy-shell.js.map +0 -1
  204. package/dist/core/tools/bash-policy-types.d.ts +0 -146
  205. package/dist/core/tools/bash-policy-types.d.ts.map +0 -1
  206. package/dist/core/tools/bash-policy-types.js +0 -2
  207. package/dist/core/tools/bash-policy-types.js.map +0 -1
  208. package/dist/core/tools/bash-policy.d.ts +0 -6
  209. package/dist/core/tools/bash-policy.d.ts.map +0 -1
  210. package/dist/core/tools/bash-policy.js +0 -5
  211. package/dist/core/tools/bash-policy.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/atomic",
3
- "version": "0.9.0-alpha.1",
3
+ "version": "0.9.0-alpha.2",
4
4
  "description": "Atomic coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "atomicConfig": {
@@ -68,11 +68,11 @@
68
68
  "prepublishOnly": "bun run clean && bun run build"
69
69
  },
70
70
  "dependencies": {
71
- "@bastani/atomic-natives": "0.9.0-alpha.1",
71
+ "@bastani/atomic-natives": "0.9.0-alpha.2",
72
72
  "@bufbuild/protobuf": "^2.0.0",
73
- "@earendil-works/pi-agent-core": "^0.79.7",
74
- "@earendil-works/pi-ai": "^0.79.7",
75
- "@earendil-works/pi-tui": "^0.79.7",
73
+ "@earendil-works/pi-agent-core": "^0.79.9",
74
+ "@earendil-works/pi-ai": "^0.79.9",
75
+ "@earendil-works/pi-tui": "^0.79.9",
76
76
  "@modelcontextprotocol/ext-apps": "^1.7.2",
77
77
  "@modelcontextprotocol/sdk": "^1.25.1",
78
78
  "@mozilla/readability": "^0.6.0",
@@ -1,277 +0,0 @@
1
- /**
2
- * Workflow definition builder.
3
- * Authoring API: defineWorkflow(name).description(...).input(...).run(fn).compile()
4
- *
5
- * Immutable/chained semantics: every builder method returns a NEW builder
6
- * instance; the previous instance is unchanged.
7
- *
8
- * cross-ref: v0.x packages/atomic-sdk/src/define-workflow.ts
9
- */
10
-
11
- import type { Static, TOptional, TSchema } from "typebox";
12
- import type * as AuthoringContract from "../shared/authoring-contract.js";
13
- import type {
14
- WorkflowDefinition,
15
- WorkflowInputBindings,
16
- WorkflowInputValues,
17
- WorkflowOutputValues,
18
- WorkflowRunContext,
19
- WorkflowSerializableValue,
20
- WorkflowRunFn,
21
- WorkflowWorktreeInputBinding,
22
- } from "../shared/types.js";
23
- import { normalizeWorkflowName } from "./identity.js";
24
-
25
- const BRANDED_WORKFLOW_DEFINITIONS = new WeakSet<object>();
26
-
27
- // Package-internal runtime brand. It deliberately is not exported through the
28
- // public SDK surface; only defineWorkflow(...).compile() and executor-created
29
- // direct workflows can mint discoverable definitions.
30
- export function stampWorkflowDefinition<
31
- TInputs extends WorkflowInputValues,
32
- TOutputs extends WorkflowOutputValues,
33
- >(
34
- definition: WorkflowDefinition<TInputs, TOutputs>,
35
- ): WorkflowDefinition<TInputs, TOutputs> {
36
- BRANDED_WORKFLOW_DEFINITIONS.add(definition);
37
- return definition;
38
- }
39
-
40
- export function isBrandedWorkflowDefinition(value: unknown): value is WorkflowDefinition {
41
- return value !== null &&
42
- typeof value === "object" &&
43
- BRANDED_WORKFLOW_DEFINITIONS.has(value);
44
- }
45
-
46
- // ---------------------------------------------------------------------------
47
- // Type inference helpers (TypeBox Static<> mapping)
48
- // ---------------------------------------------------------------------------
49
-
50
- /**
51
- * One declared key as a single-key object type. An `Type.Optional(...)` schema
52
- * makes the KEY optional (so access yields `T | undefined`); every other schema
53
- * — including a defaulted one — makes the key required (defaults are always
54
- * present at runtime after they are applied). A schema `default` is not
55
- * detectable at the type level, which is the correct behavior here.
56
- */
57
- type DeclaredEntry<K extends string, S extends TSchema> =
58
- S extends TOptional<TSchema>
59
- ? { readonly [P in K]?: Static<S> }
60
- : { readonly [P in K]: Static<S> };
61
-
62
- /** Collapse an accumulated intersection into a single, readable object type. */
63
- type Simplify<T> = { [K in keyof T]: T[K] } & {};
64
-
65
- type SimplifyWorkflowOutputs<T> = Simplify<T>;
66
- type DeclaredOutputEntry<K extends string, S extends TSchema> =
67
- S extends TOptional<TSchema>
68
- ? { readonly [P in K]?: Static<S> & WorkflowSerializableValue }
69
- : { readonly [P in K]: Static<S> & WorkflowSerializableValue };
70
-
71
- type AccumulateWorkflowOutput<TOutputs, K extends string, S extends TSchema> = Simplify<
72
- string extends keyof TOutputs
73
- ? DeclaredOutputEntry<K, S>
74
- : TOutputs & DeclaredOutputEntry<K, S>
75
- >;
76
-
77
- interface BuilderState<TInputs extends WorkflowInputValues> {
78
- readonly name: string;
79
- readonly description: string;
80
- readonly inputs: Readonly<Record<string, TSchema>>;
81
- readonly outputs: Readonly<Record<string, TSchema>>;
82
- readonly inputBindings: WorkflowInputBindings;
83
- // Stored type-erased on outputs: the builder threads the precise output map
84
- // through its public interface, but the immutable state survives across
85
- // generic changes, so it keeps the loose run-fn type and re-applies the
86
- // precise type at .run()/.compile() boundaries via casts.
87
- readonly runFn: WorkflowRunFn<TInputs, WorkflowOutputValues> | undefined;
88
- }
89
-
90
- // ---------------------------------------------------------------------------
91
- // Public builder interfaces — split so .compile() only appears after .run()
92
- // ---------------------------------------------------------------------------
93
-
94
- /**
95
- * Builder returned by defineWorkflow(name) before .run() is called.
96
- * Allows chaining .description() and .input() in any order; .run() seals
97
- * the run function and returns a CompletedWorkflowBuilder.
98
- *
99
- * TInputs defaults to serializable input values so compiled definitions stay
100
- * compatible with the type-erased registry without casts.
101
- */
102
- export interface WorkflowBuilder<
103
- TInputs extends WorkflowInputValues = {},
104
- TOutputs extends WorkflowOutputValues = {},
105
- > extends Omit<
106
- AuthoringContract.WorkflowBuilder<TInputs, TOutputs>,
107
- "description" | "input" | "output" | "worktreeFromInputs" | "run"
108
- > {
109
- description(text: string): WorkflowBuilder<TInputs, TOutputs>;
110
- input<K extends string, S extends TSchema>(key: K, schema: S): WorkflowBuilder<TInputs & DeclaredEntry<K, S>, TOutputs>;
111
- output<K extends string, S extends TSchema>(key: K, schema: S): WorkflowBuilder<TInputs, AccumulateWorkflowOutput<TOutputs, K, S>>;
112
- worktreeFromInputs(binding: WorkflowWorktreeInputBinding): WorkflowBuilder<TInputs, TOutputs>;
113
- run<TActualOutputs extends SimplifyWorkflowOutputs<TOutputs>>(
114
- fn: (ctx: WorkflowRunContext<Simplify<TInputs>, SimplifyWorkflowOutputs<TOutputs>>) => Promise<AuthoringContract.NoExtraOutputs<SimplifyWorkflowOutputs<TOutputs>, TActualOutputs>> | AuthoringContract.NoExtraOutputs<SimplifyWorkflowOutputs<TOutputs>, TActualOutputs>,
115
- ): CompletedWorkflowBuilder<TInputs, TOutputs>;
116
- }
117
-
118
- /**
119
- * Builder returned after .run() is called.
120
- * Still allows chaining .description() and .input(); .compile() is now available.
121
- */
122
- export interface CompletedWorkflowBuilder<
123
- TInputs extends WorkflowInputValues,
124
- TOutputs extends WorkflowOutputValues,
125
- > extends Omit<
126
- AuthoringContract.CompletedWorkflowBuilder<TInputs, TOutputs>,
127
- "description" | "input" | "output" | "worktreeFromInputs" | "run" | "compile"
128
- > {
129
- description(text: string): CompletedWorkflowBuilder<TInputs, TOutputs>;
130
- input<K extends string, S extends TSchema>(key: K, schema: S): CompletedWorkflowBuilder<TInputs & DeclaredEntry<K, S>, TOutputs>;
131
- output<K extends string, S extends TSchema>(key: K, schema: S): CompletedWorkflowBuilder<TInputs, AccumulateWorkflowOutput<TOutputs, K, S>>;
132
- worktreeFromInputs(binding: WorkflowWorktreeInputBinding): CompletedWorkflowBuilder<TInputs, TOutputs>;
133
- run<TActualOutputs extends SimplifyWorkflowOutputs<TOutputs>>(
134
- fn: (ctx: WorkflowRunContext<Simplify<TInputs>, SimplifyWorkflowOutputs<TOutputs>>) => Promise<AuthoringContract.NoExtraOutputs<SimplifyWorkflowOutputs<TOutputs>, TActualOutputs>> | AuthoringContract.NoExtraOutputs<SimplifyWorkflowOutputs<TOutputs>, TActualOutputs>,
135
- ): CompletedWorkflowBuilder<TInputs, TOutputs>;
136
- compile(): WorkflowDefinition<Simplify<TInputs>, SimplifyWorkflowOutputs<TOutputs>>;
137
- }
138
-
139
- // ---------------------------------------------------------------------------
140
- // Internal factory — constructs a builder from immutable state
141
- // ---------------------------------------------------------------------------
142
-
143
- function requireNonEmptyString(value: string, label: string): void {
144
- if (typeof value !== "string" || value.trim().length === 0) {
145
- throw new TypeError(`defineWorkflow: ${label} must be a non-empty string`);
146
- }
147
- }
148
-
149
- // Freeze only the top-level map. The per-key TypeBox schemas are shared,
150
- // internally-symbol-keyed objects and must not be shallow-cloned (that would
151
- // drop the Kind/Optional symbols the runtime validator relies on).
152
- function freezeSchemaMap(
153
- schemas: Readonly<Record<string, TSchema>>,
154
- ): Readonly<Record<string, TSchema>> {
155
- return Object.freeze({ ...schemas });
156
- }
157
-
158
- function makeBuilder<
159
- TInputs extends WorkflowInputValues,
160
- TOutputs extends WorkflowOutputValues,
161
- >(
162
- state: BuilderState<TInputs>,
163
- ): WorkflowBuilder<TInputs, TOutputs> & CompletedWorkflowBuilder<TInputs, TOutputs> {
164
- return {
165
- description(text: string) {
166
- return makeBuilder<TInputs, TOutputs>({ ...state, description: text });
167
- },
168
-
169
- input<K extends string, S extends TSchema>(key: K, schema: S) {
170
- requireNonEmptyString(key, "input key");
171
- return makeBuilder<TInputs & DeclaredEntry<K, S>, TOutputs>({
172
- ...state,
173
- inputs: { ...state.inputs, [key]: schema },
174
- } as BuilderState<TInputs & DeclaredEntry<K, S>>);
175
- },
176
-
177
- output<K extends string, S extends TSchema>(key: K, schema: S) {
178
- requireNonEmptyString(key, "output key");
179
- return makeBuilder<TInputs, AccumulateWorkflowOutput<TOutputs, K, S>>({
180
- ...state,
181
- outputs: { ...state.outputs, [key]: schema },
182
- });
183
- },
184
-
185
- worktreeFromInputs(binding: WorkflowWorktreeInputBinding) {
186
- return makeBuilder<TInputs, TOutputs>({
187
- ...state,
188
- inputBindings: {
189
- ...state.inputBindings,
190
- worktree: { ...binding },
191
- },
192
- });
193
- },
194
-
195
- run<TActualOutputs extends SimplifyWorkflowOutputs<TOutputs>>(
196
- fn: (ctx: WorkflowRunContext<Simplify<TInputs>, SimplifyWorkflowOutputs<TOutputs>>) => Promise<AuthoringContract.NoExtraOutputs<SimplifyWorkflowOutputs<TOutputs>, TActualOutputs>> | AuthoringContract.NoExtraOutputs<SimplifyWorkflowOutputs<TOutputs>, TActualOutputs>,
197
- ) {
198
- return makeBuilder<TInputs, TOutputs>({
199
- ...state,
200
- runFn: fn as unknown as WorkflowRunFn<TInputs, WorkflowOutputValues>,
201
- });
202
- },
203
-
204
- compile(): WorkflowDefinition<Simplify<TInputs>, SimplifyWorkflowOutputs<TOutputs>> {
205
- if (!state.runFn) {
206
- throw new Error(
207
- `defineWorkflow("${state.name}"): .run(fn) must be called before .compile()`,
208
- );
209
- }
210
-
211
- const normalizedName = normalizeWorkflowName(state.name);
212
-
213
- // Deep-freeze nested maps first, then the top-level definition.
214
- const frozenInputs = freezeSchemaMap(state.inputs);
215
- const frozenOutputs = freezeSchemaMap(state.outputs);
216
- const inputBindings = Object.freeze({
217
- ...state.inputBindings,
218
- ...(state.inputBindings.worktree !== undefined
219
- ? { worktree: Object.freeze({ ...state.inputBindings.worktree }) }
220
- : {}),
221
- });
222
-
223
- const definition = {
224
- __piWorkflow: true,
225
- name: state.name,
226
- normalizedName,
227
- description: state.description,
228
- inputs: frozenInputs,
229
- ...(Object.keys(frozenOutputs).length > 0 ? { outputs: frozenOutputs } : {}),
230
- ...(Object.keys(inputBindings).length > 0 ? { inputBindings } : {}),
231
- run: state.runFn as unknown as WorkflowRunFn<Simplify<TInputs>, SimplifyWorkflowOutputs<TOutputs>>,
232
- } as WorkflowDefinition<Simplify<TInputs>, SimplifyWorkflowOutputs<TOutputs>>;
233
-
234
- // Stamp before freezing so the WeakSet brand can be attached.
235
- stampWorkflowDefinition(definition);
236
- return Object.freeze(definition) as WorkflowDefinition<Simplify<TInputs>, SimplifyWorkflowOutputs<TOutputs>>;
237
- },
238
- };
239
- }
240
-
241
- // ---------------------------------------------------------------------------
242
- // Public entry point
243
- // ---------------------------------------------------------------------------
244
-
245
- /**
246
- * Start building a workflow definition.
247
- *
248
- * @example
249
- * import { defineWorkflow, Type } from "@bastani/workflows";
250
- *
251
- * export default defineWorkflow("deep-research-codebase")
252
- * .description("Scout → specialists → aggregator")
253
- * .input("prompt", Type.String({ description: "research question" }))
254
- * .input("max_partitions", Type.Number({ default: 4 }))
255
- * .run(async (ctx) => {
256
- * const scout = ctx.stage("scout");
257
- * const findings = await scout.prompt(`Scout: ${ctx.inputs.prompt}`);
258
- * return { findings };
259
- * })
260
- * .compile();
261
- */
262
- export function defineWorkflow(name: string): WorkflowBuilder {
263
- if (!name || typeof name !== "string") {
264
- throw new TypeError("defineWorkflow: name must be a non-empty string");
265
- }
266
-
267
- const initialState: BuilderState<{}> = {
268
- name,
269
- description: "",
270
- inputs: {},
271
- outputs: {},
272
- inputBindings: {},
273
- runFn: undefined,
274
- };
275
-
276
- return makeBuilder<{}, {}>(initialState);
277
- }
@@ -1,5 +0,0 @@
1
- import type { BashCommandPolicy, BashCommandPolicyMatchMode, CompileResult } from "./bash-policy-types.ts";
2
- export declare function compilePolicy(policy: BashCommandPolicy): CompileResult;
3
- export declare function validateBashCommandPolicy(policy: BashCommandPolicy): void;
4
- export declare function invalidPolicyMode(policy: BashCommandPolicy): BashCommandPolicyMatchMode;
5
- //# sourceMappingURL=bash-policy-compile.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"bash-policy-compile.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash-policy-compile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,iBAAiB,EACjB,0BAA0B,EAG1B,aAAa,EAGb,MAAM,wBAAwB,CAAC;AAqNhC,wBAAgB,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,aAAa,CA2CtE;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAKzE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,0BAA0B,CAGvF","sourcesContent":["import type {\n\tBashCommandPolicy,\n\tBashCommandPolicyMatchMode,\n\tBashCommandRule,\n\tCompiledRule,\n\tCompileResult,\n\tCompileRuleResult,\n\tCompileRulesResult,\n} from \"./bash-policy-types.ts\";\n\ntype RuntimePropertyValue =\n\t| string\n\t| number\n\t| boolean\n\t| bigint\n\t| symbol\n\t| null\n\t| object\n\t| ((...args: readonly never[]) => RuntimePropertyValue)\n\t| undefined;\n\ntype RuleObjectKey = \"prefix\" | \"glob\" | \"regex\" | \"flags\";\n\nconst BASH_POLICY_TOP_LEVEL_KEYS = [\"default\", \"allow\", \"deny\", \"match\"] as const;\nconst BASH_POLICY_TOP_LEVEL_KEY_SET: ReadonlySet<string> = new Set(BASH_POLICY_TOP_LEVEL_KEYS);\n\nfunction hasOwnRuleKey<Key extends RuleObjectKey>(\n\tvalue: object,\n\tkey: Key,\n): value is Record<Key, RuntimePropertyValue> {\n\treturn Object.prototype.hasOwnProperty.call(value, key);\n}\n\nfunction hasOnlyRuleKeys(value: object, allowedKeys: readonly string[]): boolean {\n\treturn Object.keys(value).every((key) => allowedKeys.includes(key));\n}\n\nfunction unknownBashPolicyTopLevelKeys(policy: object): readonly string[] {\n\treturn Object.keys(policy).filter((key) => !BASH_POLICY_TOP_LEVEL_KEY_SET.has(key));\n}\n\nfunction escapeRegexLiteral(value: string): string {\n\treturn value.replace(/[\\\\^$.*+?()[\\]{}|]/g, \"\\\\$&\");\n}\n\nfunction escapeGlobClassCharacter(char: string): string {\n\tif (char === \"\\\\\") return \"\\\\\\\\\";\n\tif (char === \"]\") return \"\\\\]\";\n\tif (char === \"[\") return \"\\\\[\";\n\treturn char;\n}\n\nfunction escapeEscapedGlobClassCharacter(char: string): string {\n\tif (char === \"\\\\\") return \"\\\\\\\\\";\n\tif (char === \"-\") return \"\\\\-\";\n\tif (char === \"^\") return \"\\\\^\";\n\tif (char === \"]\") return \"\\\\]\";\n\tif (char === \"[\") return \"\\\\[\";\n\treturn char;\n}\n\nfunction readGlobBracketClass(\n\tpattern: string,\n\topenIndex: number,\n): { readonly regexSource: string; readonly closeIndex: number } | undefined {\n\tlet cursor = openIndex + 1;\n\tlet negated = false;\n\tlet hasContent = false;\n\tlet content = \"\";\n\n\tif (cursor >= pattern.length) return undefined;\n\tif (pattern[cursor] === \"!\" || pattern[cursor] === \"^\") {\n\t\tnegated = true;\n\t\tcursor += 1;\n\t}\n\tif (pattern[cursor] === \"]\") {\n\t\tcontent += \"\\\\]\";\n\t\thasContent = true;\n\t\tcursor += 1;\n\t}\n\n\tfor (; cursor < pattern.length; cursor += 1) {\n\t\tconst char = pattern[cursor]!;\n\t\tif (char === \"]\") {\n\t\t\tif (!hasContent) return undefined;\n\t\t\treturn { regexSource: `[${negated ? \"^\" : \"\"}${content}]`, closeIndex: cursor };\n\t\t}\n\t\thasContent = true;\n\t\tif (char === \"\\\\\" && cursor + 1 < pattern.length) {\n\t\t\tcursor += 1;\n\t\t\tcontent += escapeEscapedGlobClassCharacter(pattern[cursor]!);\n\t\t\tcontinue;\n\t\t}\n\t\tcontent += escapeGlobClassCharacter(char);\n\t}\n\n\treturn undefined;\n}\n\nfunction compileCommandStringGlob(pattern: string): RegExp {\n\tlet source = \"^\";\n\tfor (let index = 0; index < pattern.length; index += 1) {\n\t\tconst char = pattern[index]!;\n\t\tif (char === \"*\") {\n\t\t\tsource += \".*\";\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === \"?\") {\n\t\t\tsource += \".\";\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === \"[\") {\n\t\t\tconst bracket = readGlobBracketClass(pattern, index);\n\t\t\tif (bracket !== undefined) {\n\t\t\t\tsource += bracket.regexSource;\n\t\t\t\tindex = bracket.closeIndex;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tif (char === \"\\\\\" && index + 1 < pattern.length) {\n\t\t\tindex += 1;\n\t\t\tsource += escapeRegexLiteral(pattern[index]!);\n\t\t\tcontinue;\n\t\t}\n\t\tsource += escapeRegexLiteral(char);\n\t}\n\treturn new RegExp(`${source}$`);\n}\n\nfunction compileRule(rule: BashCommandRule, listName: \"allow\" | \"deny\", index: number): CompileRuleResult {\n\tif (typeof rule === \"string\") {\n\t\tif (rule.length === 0) {\n\t\t\treturn { ok: false, message: `${listName}[${index}] exact rule must not be empty` };\n\t\t}\n\t\treturn { ok: true, rule: { kind: \"exact\", source: rule, value: rule } };\n\t}\n\n\tif (typeof rule !== \"object\" || rule === null || Array.isArray(rule)) {\n\t\treturn { ok: false, message: `${listName}[${index}] rule must be a non-empty string or an object rule` };\n\t}\n\n\tconst hasPrefix = hasOwnRuleKey(rule, \"prefix\");\n\tconst hasGlob = hasOwnRuleKey(rule, \"glob\");\n\tconst hasRegex = hasOwnRuleKey(rule, \"regex\");\n\tconst variantCount = (hasPrefix ? 1 : 0) + (hasGlob ? 1 : 0) + (hasRegex ? 1 : 0);\n\tif (variantCount !== 1) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `${listName}[${index}] must specify exactly one of prefix, glob, or regex`,\n\t\t};\n\t}\n\n\tif (hasPrefix) {\n\t\tif (!hasOnlyRuleKeys(rule, [\"prefix\"])) {\n\t\t\treturn { ok: false, message: `${listName}[${index}] prefix rule must only contain prefix` };\n\t\t}\n\t\tif (typeof rule.prefix !== \"string\") {\n\t\t\treturn { ok: false, message: `${listName}[${index}].prefix must be a string` };\n\t\t}\n\t\tif (rule.prefix.length === 0) {\n\t\t\treturn { ok: false, message: `${listName}[${index}].prefix must not be empty` };\n\t\t}\n\t\treturn { ok: true, rule: { kind: \"prefix\", source: rule, value: rule.prefix } };\n\t}\n\n\tif (hasGlob) {\n\t\tif (!hasOnlyRuleKeys(rule, [\"glob\"])) {\n\t\t\treturn { ok: false, message: `${listName}[${index}] glob rule must only contain glob` };\n\t\t}\n\t\tif (typeof rule.glob !== \"string\") {\n\t\t\treturn { ok: false, message: `${listName}[${index}].glob must be a string` };\n\t\t}\n\t\tif (rule.glob.length === 0) {\n\t\t\treturn { ok: false, message: `${listName}[${index}].glob must not be empty` };\n\t\t}\n\t\ttry {\n\t\t\treturn { ok: true, rule: { kind: \"glob\", source: rule, value: compileCommandStringGlob(rule.glob) } };\n\t\t} catch {\n\t\t\treturn { ok: false, message: `${listName}[${index}].glob is not a valid command string glob` };\n\t\t}\n\t}\n\n\tif (!hasOnlyRuleKeys(rule, [\"regex\", \"flags\"])) {\n\t\treturn { ok: false, message: `${listName}[${index}] regex rule must only contain regex and optional flags` };\n\t}\n\tif (typeof rule.regex !== \"string\") {\n\t\treturn { ok: false, message: `${listName}[${index}].regex must be a string` };\n\t}\n\tif (rule.regex.length === 0) {\n\t\treturn { ok: false, message: `${listName}[${index}].regex must not be empty` };\n\t}\n\tconst hasFlags = hasOwnRuleKey(rule, \"flags\");\n\tif (hasFlags && typeof rule.flags !== \"string\") {\n\t\treturn { ok: false, message: `${listName}[${index}].flags must be a string when present` };\n\t}\n\tconst flags = hasFlags && typeof rule.flags === \"string\" ? rule.flags : \"\";\n\tif (/[gy]/.test(flags)) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `${listName}[${index}].flags must not include stateful g or y regex flags`,\n\t\t};\n\t}\n\ttry {\n\t\treturn { ok: true, rule: { kind: \"regex\", source: rule, value: new RegExp(rule.regex, flags) } };\n\t} catch {\n\t\treturn { ok: false, message: `${listName}[${index}].regex is not a valid JavaScript RegExp` };\n\t}\n}\n\nfunction compileRules(rules: readonly BashCommandRule[] | undefined, listName: \"allow\" | \"deny\"): CompileRulesResult {\n\tconst compiled: CompiledRule[] = [];\n\tif (rules === undefined) return { ok: true, rules: compiled };\n\tfor (let index = 0; index < rules.length; index += 1) {\n\t\tconst rule = rules[index]!;\n\t\tconst result = compileRule(rule, listName, index);\n\t\tif (!result.ok) return result;\n\t\tcompiled.push(result.rule);\n\t}\n\treturn { ok: true, rules: compiled };\n}\n\nexport function compilePolicy(policy: BashCommandPolicy): CompileResult {\n\tif (typeof policy !== \"object\" || policy === null || Array.isArray(policy)) {\n\t\treturn { ok: false, message: \"bash policy must be a non-null object\" };\n\t}\n\n\tconst unknownKeys = unknownBashPolicyTopLevelKeys(policy);\n\tif (unknownKeys.length > 0) {\n\t\tconst formattedKeys = unknownKeys.map((key) => JSON.stringify(key)).join(\", \");\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `bash policy contains unknown top-level key${unknownKeys.length === 1 ? \"\" : \"s\"} ${formattedKeys}; allowed keys are default, allow, deny, and match`,\n\t\t};\n\t}\n\n\tconst defaultDecision = policy.default === undefined ? \"allow\" : policy.default;\n\tif (defaultDecision !== \"allow\" && defaultDecision !== \"deny\") {\n\t\treturn { ok: false, message: `bash policy default must be \"allow\" or \"deny\"` };\n\t}\n\tconst match = policy.match === undefined ? \"segments\" : policy.match;\n\tif (match !== \"whole\" && match !== \"segments\") {\n\t\treturn { ok: false, message: `bash policy match must be \"whole\" or \"segments\"` };\n\t}\n\tif (policy.allow !== undefined && !Array.isArray(policy.allow)) {\n\t\treturn { ok: false, message: \"bash policy allow must be an array\" };\n\t}\n\tif (policy.deny !== undefined && !Array.isArray(policy.deny)) {\n\t\treturn { ok: false, message: \"bash policy deny must be an array\" };\n\t}\n\n\tconst allow = compileRules(policy.allow, \"allow\");\n\tif (!allow.ok) return allow;\n\tconst deny = compileRules(policy.deny, \"deny\");\n\tif (!deny.ok) return deny;\n\n\treturn {\n\t\tok: true,\n\t\tpolicy: {\n\t\t\tdefaultDecision,\n\t\t\tmatch,\n\t\t\tallow: allow.rules,\n\t\t\tdeny: deny.rules,\n\t\t},\n\t};\n}\n\nexport function validateBashCommandPolicy(policy: BashCommandPolicy): void {\n\tconst compiled = compilePolicy(policy);\n\tif (!compiled.ok) {\n\t\tthrow new Error(`Invalid bash command policy: ${compiled.message}`);\n\t}\n}\n\nexport function invalidPolicyMode(policy: BashCommandPolicy): BashCommandPolicyMatchMode {\n\tif (typeof policy !== \"object\" || policy === null || Array.isArray(policy)) return \"segments\";\n\treturn policy.match === \"whole\" ? \"whole\" : \"segments\";\n}\n"]}
@@ -1,241 +0,0 @@
1
- const BASH_POLICY_TOP_LEVEL_KEYS = ["default", "allow", "deny", "match"];
2
- const BASH_POLICY_TOP_LEVEL_KEY_SET = new Set(BASH_POLICY_TOP_LEVEL_KEYS);
3
- function hasOwnRuleKey(value, key) {
4
- return Object.prototype.hasOwnProperty.call(value, key);
5
- }
6
- function hasOnlyRuleKeys(value, allowedKeys) {
7
- return Object.keys(value).every((key) => allowedKeys.includes(key));
8
- }
9
- function unknownBashPolicyTopLevelKeys(policy) {
10
- return Object.keys(policy).filter((key) => !BASH_POLICY_TOP_LEVEL_KEY_SET.has(key));
11
- }
12
- function escapeRegexLiteral(value) {
13
- return value.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
14
- }
15
- function escapeGlobClassCharacter(char) {
16
- if (char === "\\")
17
- return "\\\\";
18
- if (char === "]")
19
- return "\\]";
20
- if (char === "[")
21
- return "\\[";
22
- return char;
23
- }
24
- function escapeEscapedGlobClassCharacter(char) {
25
- if (char === "\\")
26
- return "\\\\";
27
- if (char === "-")
28
- return "\\-";
29
- if (char === "^")
30
- return "\\^";
31
- if (char === "]")
32
- return "\\]";
33
- if (char === "[")
34
- return "\\[";
35
- return char;
36
- }
37
- function readGlobBracketClass(pattern, openIndex) {
38
- let cursor = openIndex + 1;
39
- let negated = false;
40
- let hasContent = false;
41
- let content = "";
42
- if (cursor >= pattern.length)
43
- return undefined;
44
- if (pattern[cursor] === "!" || pattern[cursor] === "^") {
45
- negated = true;
46
- cursor += 1;
47
- }
48
- if (pattern[cursor] === "]") {
49
- content += "\\]";
50
- hasContent = true;
51
- cursor += 1;
52
- }
53
- for (; cursor < pattern.length; cursor += 1) {
54
- const char = pattern[cursor];
55
- if (char === "]") {
56
- if (!hasContent)
57
- return undefined;
58
- return { regexSource: `[${negated ? "^" : ""}${content}]`, closeIndex: cursor };
59
- }
60
- hasContent = true;
61
- if (char === "\\" && cursor + 1 < pattern.length) {
62
- cursor += 1;
63
- content += escapeEscapedGlobClassCharacter(pattern[cursor]);
64
- continue;
65
- }
66
- content += escapeGlobClassCharacter(char);
67
- }
68
- return undefined;
69
- }
70
- function compileCommandStringGlob(pattern) {
71
- let source = "^";
72
- for (let index = 0; index < pattern.length; index += 1) {
73
- const char = pattern[index];
74
- if (char === "*") {
75
- source += ".*";
76
- continue;
77
- }
78
- if (char === "?") {
79
- source += ".";
80
- continue;
81
- }
82
- if (char === "[") {
83
- const bracket = readGlobBracketClass(pattern, index);
84
- if (bracket !== undefined) {
85
- source += bracket.regexSource;
86
- index = bracket.closeIndex;
87
- continue;
88
- }
89
- }
90
- if (char === "\\" && index + 1 < pattern.length) {
91
- index += 1;
92
- source += escapeRegexLiteral(pattern[index]);
93
- continue;
94
- }
95
- source += escapeRegexLiteral(char);
96
- }
97
- return new RegExp(`${source}$`);
98
- }
99
- function compileRule(rule, listName, index) {
100
- if (typeof rule === "string") {
101
- if (rule.length === 0) {
102
- return { ok: false, message: `${listName}[${index}] exact rule must not be empty` };
103
- }
104
- return { ok: true, rule: { kind: "exact", source: rule, value: rule } };
105
- }
106
- if (typeof rule !== "object" || rule === null || Array.isArray(rule)) {
107
- return { ok: false, message: `${listName}[${index}] rule must be a non-empty string or an object rule` };
108
- }
109
- const hasPrefix = hasOwnRuleKey(rule, "prefix");
110
- const hasGlob = hasOwnRuleKey(rule, "glob");
111
- const hasRegex = hasOwnRuleKey(rule, "regex");
112
- const variantCount = (hasPrefix ? 1 : 0) + (hasGlob ? 1 : 0) + (hasRegex ? 1 : 0);
113
- if (variantCount !== 1) {
114
- return {
115
- ok: false,
116
- message: `${listName}[${index}] must specify exactly one of prefix, glob, or regex`,
117
- };
118
- }
119
- if (hasPrefix) {
120
- if (!hasOnlyRuleKeys(rule, ["prefix"])) {
121
- return { ok: false, message: `${listName}[${index}] prefix rule must only contain prefix` };
122
- }
123
- if (typeof rule.prefix !== "string") {
124
- return { ok: false, message: `${listName}[${index}].prefix must be a string` };
125
- }
126
- if (rule.prefix.length === 0) {
127
- return { ok: false, message: `${listName}[${index}].prefix must not be empty` };
128
- }
129
- return { ok: true, rule: { kind: "prefix", source: rule, value: rule.prefix } };
130
- }
131
- if (hasGlob) {
132
- if (!hasOnlyRuleKeys(rule, ["glob"])) {
133
- return { ok: false, message: `${listName}[${index}] glob rule must only contain glob` };
134
- }
135
- if (typeof rule.glob !== "string") {
136
- return { ok: false, message: `${listName}[${index}].glob must be a string` };
137
- }
138
- if (rule.glob.length === 0) {
139
- return { ok: false, message: `${listName}[${index}].glob must not be empty` };
140
- }
141
- try {
142
- return { ok: true, rule: { kind: "glob", source: rule, value: compileCommandStringGlob(rule.glob) } };
143
- }
144
- catch {
145
- return { ok: false, message: `${listName}[${index}].glob is not a valid command string glob` };
146
- }
147
- }
148
- if (!hasOnlyRuleKeys(rule, ["regex", "flags"])) {
149
- return { ok: false, message: `${listName}[${index}] regex rule must only contain regex and optional flags` };
150
- }
151
- if (typeof rule.regex !== "string") {
152
- return { ok: false, message: `${listName}[${index}].regex must be a string` };
153
- }
154
- if (rule.regex.length === 0) {
155
- return { ok: false, message: `${listName}[${index}].regex must not be empty` };
156
- }
157
- const hasFlags = hasOwnRuleKey(rule, "flags");
158
- if (hasFlags && typeof rule.flags !== "string") {
159
- return { ok: false, message: `${listName}[${index}].flags must be a string when present` };
160
- }
161
- const flags = hasFlags && typeof rule.flags === "string" ? rule.flags : "";
162
- if (/[gy]/.test(flags)) {
163
- return {
164
- ok: false,
165
- message: `${listName}[${index}].flags must not include stateful g or y regex flags`,
166
- };
167
- }
168
- try {
169
- return { ok: true, rule: { kind: "regex", source: rule, value: new RegExp(rule.regex, flags) } };
170
- }
171
- catch {
172
- return { ok: false, message: `${listName}[${index}].regex is not a valid JavaScript RegExp` };
173
- }
174
- }
175
- function compileRules(rules, listName) {
176
- const compiled = [];
177
- if (rules === undefined)
178
- return { ok: true, rules: compiled };
179
- for (let index = 0; index < rules.length; index += 1) {
180
- const rule = rules[index];
181
- const result = compileRule(rule, listName, index);
182
- if (!result.ok)
183
- return result;
184
- compiled.push(result.rule);
185
- }
186
- return { ok: true, rules: compiled };
187
- }
188
- export function compilePolicy(policy) {
189
- if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
190
- return { ok: false, message: "bash policy must be a non-null object" };
191
- }
192
- const unknownKeys = unknownBashPolicyTopLevelKeys(policy);
193
- if (unknownKeys.length > 0) {
194
- const formattedKeys = unknownKeys.map((key) => JSON.stringify(key)).join(", ");
195
- return {
196
- ok: false,
197
- message: `bash policy contains unknown top-level key${unknownKeys.length === 1 ? "" : "s"} ${formattedKeys}; allowed keys are default, allow, deny, and match`,
198
- };
199
- }
200
- const defaultDecision = policy.default === undefined ? "allow" : policy.default;
201
- if (defaultDecision !== "allow" && defaultDecision !== "deny") {
202
- return { ok: false, message: `bash policy default must be "allow" or "deny"` };
203
- }
204
- const match = policy.match === undefined ? "segments" : policy.match;
205
- if (match !== "whole" && match !== "segments") {
206
- return { ok: false, message: `bash policy match must be "whole" or "segments"` };
207
- }
208
- if (policy.allow !== undefined && !Array.isArray(policy.allow)) {
209
- return { ok: false, message: "bash policy allow must be an array" };
210
- }
211
- if (policy.deny !== undefined && !Array.isArray(policy.deny)) {
212
- return { ok: false, message: "bash policy deny must be an array" };
213
- }
214
- const allow = compileRules(policy.allow, "allow");
215
- if (!allow.ok)
216
- return allow;
217
- const deny = compileRules(policy.deny, "deny");
218
- if (!deny.ok)
219
- return deny;
220
- return {
221
- ok: true,
222
- policy: {
223
- defaultDecision,
224
- match,
225
- allow: allow.rules,
226
- deny: deny.rules,
227
- },
228
- };
229
- }
230
- export function validateBashCommandPolicy(policy) {
231
- const compiled = compilePolicy(policy);
232
- if (!compiled.ok) {
233
- throw new Error(`Invalid bash command policy: ${compiled.message}`);
234
- }
235
- }
236
- export function invalidPolicyMode(policy) {
237
- if (typeof policy !== "object" || policy === null || Array.isArray(policy))
238
- return "segments";
239
- return policy.match === "whole" ? "whole" : "segments";
240
- }
241
- //# sourceMappingURL=bash-policy-compile.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"bash-policy-compile.js","sourceRoot":"","sources":["../../../src/core/tools/bash-policy-compile.ts"],"names":[],"mappings":"AAuBA,MAAM,0BAA0B,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAU,CAAC;AAClF,MAAM,6BAA6B,GAAwB,IAAI,GAAG,CAAC,0BAA0B,CAAC,CAAC;AAE/F,SAAS,aAAa,CACrB,KAAa,EACb,GAAQ;IAER,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,eAAe,CAAC,KAAa,EAAE,WAA8B;IACrE,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,SAAS,6BAA6B,CAAC,MAAc;IACpD,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,6BAA6B,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;AACrF,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAa;IACxC,OAAO,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACrD,CAAC;AAED,SAAS,wBAAwB,CAAC,IAAY;IAC7C,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IACjC,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IAC/B,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IAC/B,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,+BAA+B,CAAC,IAAY;IACpD,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IACjC,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IAC/B,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IAC/B,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IAC/B,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IAC/B,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,oBAAoB,CAC5B,OAAe,EACf,SAAiB;IAEjB,IAAI,MAAM,GAAG,SAAS,GAAG,CAAC,CAAC;IAC3B,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,IAAI,OAAO,GAAG,EAAE,CAAC;IAEjB,IAAI,MAAM,IAAI,OAAO,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC/C,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC;QACxD,OAAO,GAAG,IAAI,CAAC;QACf,MAAM,IAAI,CAAC,CAAC;IACb,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC;QAC7B,OAAO,IAAI,KAAK,CAAC;QACjB,UAAU,GAAG,IAAI,CAAC;QAClB,MAAM,IAAI,CAAC,CAAC;IACb,CAAC;IAED,OAAO,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAE,CAAC;QAC9B,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YAClB,IAAI,CAAC,UAAU;gBAAE,OAAO,SAAS,CAAC;YAClC,OAAO,EAAE,WAAW,EAAE,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;QACjF,CAAC;QACD,UAAU,GAAG,IAAI,CAAC;QAClB,IAAI,IAAI,KAAK,IAAI,IAAI,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YAClD,MAAM,IAAI,CAAC,CAAC;YACZ,OAAO,IAAI,+BAA+B,CAAC,OAAO,CAAC,MAAM,CAAE,CAAC,CAAC;YAC7D,SAAS;QACV,CAAC;QACD,OAAO,IAAI,wBAAwB,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,SAAS,wBAAwB,CAAC,OAAe;IAChD,IAAI,MAAM,GAAG,GAAG,CAAC;IACjB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAE,CAAC;QAC7B,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YAClB,MAAM,IAAI,IAAI,CAAC;YACf,SAAS;QACV,CAAC;QACD,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YAClB,MAAM,IAAI,GAAG,CAAC;YACd,SAAS;QACV,CAAC;QACD,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YAClB,MAAM,OAAO,GAAG,oBAAoB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YACrD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC3B,MAAM,IAAI,OAAO,CAAC,WAAW,CAAC;gBAC9B,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC;gBAC3B,SAAS;YACV,CAAC;QACF,CAAC;QACD,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YACjD,KAAK,IAAI,CAAC,CAAC;YACX,MAAM,IAAI,kBAAkB,CAAC,OAAO,CAAC,KAAK,CAAE,CAAC,CAAC;YAC9C,SAAS;QACV,CAAC;QACD,MAAM,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,IAAI,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,WAAW,CAAC,IAAqB,EAAE,QAA0B,EAAE,KAAa;IACpF,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,QAAQ,IAAI,KAAK,gCAAgC,EAAE,CAAC;QACrF,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC;IACzE,CAAC;IAED,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACtE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,QAAQ,IAAI,KAAK,qDAAqD,EAAE,CAAC;IAC1G,CAAC;IAED,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9C,MAAM,YAAY,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClF,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO;YACN,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,GAAG,QAAQ,IAAI,KAAK,sDAAsD;SACnF,CAAC;IACH,CAAC;IAED,IAAI,SAAS,EAAE,CAAC;QACf,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;YACxC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,QAAQ,IAAI,KAAK,wCAAwC,EAAE,CAAC;QAC7F,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YACrC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,QAAQ,IAAI,KAAK,2BAA2B,EAAE,CAAC;QAChF,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,QAAQ,IAAI,KAAK,4BAA4B,EAAE,CAAC;QACjF,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;IACjF,CAAC;IAED,IAAI,OAAO,EAAE,CAAC;QACb,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;YACtC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,QAAQ,IAAI,KAAK,oCAAoC,EAAE,CAAC;QACzF,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACnC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,QAAQ,IAAI,KAAK,yBAAyB,EAAE,CAAC;QAC9E,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,QAAQ,IAAI,KAAK,0BAA0B,EAAE,CAAC;QAC/E,CAAC;QACD,IAAI,CAAC;YACJ,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACvG,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,QAAQ,IAAI,KAAK,2CAA2C,EAAE,CAAC;QAChG,CAAC;IACF,CAAC;IAED,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;QAChD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,QAAQ,IAAI,KAAK,yDAAyD,EAAE,CAAC;IAC9G,CAAC;IACD,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QACpC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,QAAQ,IAAI,KAAK,0BAA0B,EAAE,CAAC;IAC/E,CAAC;IACD,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,QAAQ,IAAI,KAAK,2BAA2B,EAAE,CAAC;IAChF,CAAC;IACD,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9C,IAAI,QAAQ,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,QAAQ,IAAI,KAAK,uCAAuC,EAAE,CAAC;IAC5F,CAAC;IACD,MAAM,KAAK,GAAG,QAAQ,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3E,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO;YACN,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,GAAG,QAAQ,IAAI,KAAK,sDAAsD;SACnF,CAAC;IACH,CAAC;IACD,IAAI,CAAC;QACJ,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC;IAClG,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,QAAQ,IAAI,KAAK,0CAA0C,EAAE,CAAC;IAC/F,CAAC;AACF,CAAC;AAED,SAAS,YAAY,CAAC,KAA6C,EAAE,QAA0B;IAC9F,MAAM,QAAQ,GAAmB,EAAE,CAAC;IACpC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAC9D,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;QAClD,IAAI,CAAC,MAAM,CAAC,EAAE;YAAE,OAAO,MAAM,CAAC;QAC9B,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAyB;IACtD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5E,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,uCAAuC,EAAE,CAAC;IACxE,CAAC;IAED,MAAM,WAAW,GAAG,6BAA6B,CAAC,MAAM,CAAC,CAAC;IAC1D,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/E,OAAO;YACN,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,6CAA6C,WAAW,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,aAAa,oDAAoD;SAC9J,CAAC;IACH,CAAC;IAED,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;IAChF,IAAI,eAAe,KAAK,OAAO,IAAI,eAAe,KAAK,MAAM,EAAE,CAAC;QAC/D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,+CAA+C,EAAE,CAAC;IAChF,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;IACrE,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC;QAC/C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,iDAAiD,EAAE,CAAC;IAClF,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,oCAAoC,EAAE,CAAC;IACrE,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,mCAAmC,EAAE,CAAC;IACpE,CAAC;IAED,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAClD,IAAI,CAAC,KAAK,CAAC,EAAE;QAAE,OAAO,KAAK,CAAC;IAC5B,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC/C,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IAE1B,OAAO;QACN,EAAE,EAAE,IAAI;QACR,MAAM,EAAE;YACP,eAAe;YACf,KAAK;YACL,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,IAAI,EAAE,IAAI,CAAC,KAAK;SAChB;KACD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,MAAyB;IAClE,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACvC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,gCAAgC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACrE,CAAC;AACF,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAyB;IAC1D,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,UAAU,CAAC;IAC9F,OAAO,MAAM,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC;AACxD,CAAC","sourcesContent":["import type {\n\tBashCommandPolicy,\n\tBashCommandPolicyMatchMode,\n\tBashCommandRule,\n\tCompiledRule,\n\tCompileResult,\n\tCompileRuleResult,\n\tCompileRulesResult,\n} from \"./bash-policy-types.ts\";\n\ntype RuntimePropertyValue =\n\t| string\n\t| number\n\t| boolean\n\t| bigint\n\t| symbol\n\t| null\n\t| object\n\t| ((...args: readonly never[]) => RuntimePropertyValue)\n\t| undefined;\n\ntype RuleObjectKey = \"prefix\" | \"glob\" | \"regex\" | \"flags\";\n\nconst BASH_POLICY_TOP_LEVEL_KEYS = [\"default\", \"allow\", \"deny\", \"match\"] as const;\nconst BASH_POLICY_TOP_LEVEL_KEY_SET: ReadonlySet<string> = new Set(BASH_POLICY_TOP_LEVEL_KEYS);\n\nfunction hasOwnRuleKey<Key extends RuleObjectKey>(\n\tvalue: object,\n\tkey: Key,\n): value is Record<Key, RuntimePropertyValue> {\n\treturn Object.prototype.hasOwnProperty.call(value, key);\n}\n\nfunction hasOnlyRuleKeys(value: object, allowedKeys: readonly string[]): boolean {\n\treturn Object.keys(value).every((key) => allowedKeys.includes(key));\n}\n\nfunction unknownBashPolicyTopLevelKeys(policy: object): readonly string[] {\n\treturn Object.keys(policy).filter((key) => !BASH_POLICY_TOP_LEVEL_KEY_SET.has(key));\n}\n\nfunction escapeRegexLiteral(value: string): string {\n\treturn value.replace(/[\\\\^$.*+?()[\\]{}|]/g, \"\\\\$&\");\n}\n\nfunction escapeGlobClassCharacter(char: string): string {\n\tif (char === \"\\\\\") return \"\\\\\\\\\";\n\tif (char === \"]\") return \"\\\\]\";\n\tif (char === \"[\") return \"\\\\[\";\n\treturn char;\n}\n\nfunction escapeEscapedGlobClassCharacter(char: string): string {\n\tif (char === \"\\\\\") return \"\\\\\\\\\";\n\tif (char === \"-\") return \"\\\\-\";\n\tif (char === \"^\") return \"\\\\^\";\n\tif (char === \"]\") return \"\\\\]\";\n\tif (char === \"[\") return \"\\\\[\";\n\treturn char;\n}\n\nfunction readGlobBracketClass(\n\tpattern: string,\n\topenIndex: number,\n): { readonly regexSource: string; readonly closeIndex: number } | undefined {\n\tlet cursor = openIndex + 1;\n\tlet negated = false;\n\tlet hasContent = false;\n\tlet content = \"\";\n\n\tif (cursor >= pattern.length) return undefined;\n\tif (pattern[cursor] === \"!\" || pattern[cursor] === \"^\") {\n\t\tnegated = true;\n\t\tcursor += 1;\n\t}\n\tif (pattern[cursor] === \"]\") {\n\t\tcontent += \"\\\\]\";\n\t\thasContent = true;\n\t\tcursor += 1;\n\t}\n\n\tfor (; cursor < pattern.length; cursor += 1) {\n\t\tconst char = pattern[cursor]!;\n\t\tif (char === \"]\") {\n\t\t\tif (!hasContent) return undefined;\n\t\t\treturn { regexSource: `[${negated ? \"^\" : \"\"}${content}]`, closeIndex: cursor };\n\t\t}\n\t\thasContent = true;\n\t\tif (char === \"\\\\\" && cursor + 1 < pattern.length) {\n\t\t\tcursor += 1;\n\t\t\tcontent += escapeEscapedGlobClassCharacter(pattern[cursor]!);\n\t\t\tcontinue;\n\t\t}\n\t\tcontent += escapeGlobClassCharacter(char);\n\t}\n\n\treturn undefined;\n}\n\nfunction compileCommandStringGlob(pattern: string): RegExp {\n\tlet source = \"^\";\n\tfor (let index = 0; index < pattern.length; index += 1) {\n\t\tconst char = pattern[index]!;\n\t\tif (char === \"*\") {\n\t\t\tsource += \".*\";\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === \"?\") {\n\t\t\tsource += \".\";\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === \"[\") {\n\t\t\tconst bracket = readGlobBracketClass(pattern, index);\n\t\t\tif (bracket !== undefined) {\n\t\t\t\tsource += bracket.regexSource;\n\t\t\t\tindex = bracket.closeIndex;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tif (char === \"\\\\\" && index + 1 < pattern.length) {\n\t\t\tindex += 1;\n\t\t\tsource += escapeRegexLiteral(pattern[index]!);\n\t\t\tcontinue;\n\t\t}\n\t\tsource += escapeRegexLiteral(char);\n\t}\n\treturn new RegExp(`${source}$`);\n}\n\nfunction compileRule(rule: BashCommandRule, listName: \"allow\" | \"deny\", index: number): CompileRuleResult {\n\tif (typeof rule === \"string\") {\n\t\tif (rule.length === 0) {\n\t\t\treturn { ok: false, message: `${listName}[${index}] exact rule must not be empty` };\n\t\t}\n\t\treturn { ok: true, rule: { kind: \"exact\", source: rule, value: rule } };\n\t}\n\n\tif (typeof rule !== \"object\" || rule === null || Array.isArray(rule)) {\n\t\treturn { ok: false, message: `${listName}[${index}] rule must be a non-empty string or an object rule` };\n\t}\n\n\tconst hasPrefix = hasOwnRuleKey(rule, \"prefix\");\n\tconst hasGlob = hasOwnRuleKey(rule, \"glob\");\n\tconst hasRegex = hasOwnRuleKey(rule, \"regex\");\n\tconst variantCount = (hasPrefix ? 1 : 0) + (hasGlob ? 1 : 0) + (hasRegex ? 1 : 0);\n\tif (variantCount !== 1) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `${listName}[${index}] must specify exactly one of prefix, glob, or regex`,\n\t\t};\n\t}\n\n\tif (hasPrefix) {\n\t\tif (!hasOnlyRuleKeys(rule, [\"prefix\"])) {\n\t\t\treturn { ok: false, message: `${listName}[${index}] prefix rule must only contain prefix` };\n\t\t}\n\t\tif (typeof rule.prefix !== \"string\") {\n\t\t\treturn { ok: false, message: `${listName}[${index}].prefix must be a string` };\n\t\t}\n\t\tif (rule.prefix.length === 0) {\n\t\t\treturn { ok: false, message: `${listName}[${index}].prefix must not be empty` };\n\t\t}\n\t\treturn { ok: true, rule: { kind: \"prefix\", source: rule, value: rule.prefix } };\n\t}\n\n\tif (hasGlob) {\n\t\tif (!hasOnlyRuleKeys(rule, [\"glob\"])) {\n\t\t\treturn { ok: false, message: `${listName}[${index}] glob rule must only contain glob` };\n\t\t}\n\t\tif (typeof rule.glob !== \"string\") {\n\t\t\treturn { ok: false, message: `${listName}[${index}].glob must be a string` };\n\t\t}\n\t\tif (rule.glob.length === 0) {\n\t\t\treturn { ok: false, message: `${listName}[${index}].glob must not be empty` };\n\t\t}\n\t\ttry {\n\t\t\treturn { ok: true, rule: { kind: \"glob\", source: rule, value: compileCommandStringGlob(rule.glob) } };\n\t\t} catch {\n\t\t\treturn { ok: false, message: `${listName}[${index}].glob is not a valid command string glob` };\n\t\t}\n\t}\n\n\tif (!hasOnlyRuleKeys(rule, [\"regex\", \"flags\"])) {\n\t\treturn { ok: false, message: `${listName}[${index}] regex rule must only contain regex and optional flags` };\n\t}\n\tif (typeof rule.regex !== \"string\") {\n\t\treturn { ok: false, message: `${listName}[${index}].regex must be a string` };\n\t}\n\tif (rule.regex.length === 0) {\n\t\treturn { ok: false, message: `${listName}[${index}].regex must not be empty` };\n\t}\n\tconst hasFlags = hasOwnRuleKey(rule, \"flags\");\n\tif (hasFlags && typeof rule.flags !== \"string\") {\n\t\treturn { ok: false, message: `${listName}[${index}].flags must be a string when present` };\n\t}\n\tconst flags = hasFlags && typeof rule.flags === \"string\" ? rule.flags : \"\";\n\tif (/[gy]/.test(flags)) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `${listName}[${index}].flags must not include stateful g or y regex flags`,\n\t\t};\n\t}\n\ttry {\n\t\treturn { ok: true, rule: { kind: \"regex\", source: rule, value: new RegExp(rule.regex, flags) } };\n\t} catch {\n\t\treturn { ok: false, message: `${listName}[${index}].regex is not a valid JavaScript RegExp` };\n\t}\n}\n\nfunction compileRules(rules: readonly BashCommandRule[] | undefined, listName: \"allow\" | \"deny\"): CompileRulesResult {\n\tconst compiled: CompiledRule[] = [];\n\tif (rules === undefined) return { ok: true, rules: compiled };\n\tfor (let index = 0; index < rules.length; index += 1) {\n\t\tconst rule = rules[index]!;\n\t\tconst result = compileRule(rule, listName, index);\n\t\tif (!result.ok) return result;\n\t\tcompiled.push(result.rule);\n\t}\n\treturn { ok: true, rules: compiled };\n}\n\nexport function compilePolicy(policy: BashCommandPolicy): CompileResult {\n\tif (typeof policy !== \"object\" || policy === null || Array.isArray(policy)) {\n\t\treturn { ok: false, message: \"bash policy must be a non-null object\" };\n\t}\n\n\tconst unknownKeys = unknownBashPolicyTopLevelKeys(policy);\n\tif (unknownKeys.length > 0) {\n\t\tconst formattedKeys = unknownKeys.map((key) => JSON.stringify(key)).join(\", \");\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `bash policy contains unknown top-level key${unknownKeys.length === 1 ? \"\" : \"s\"} ${formattedKeys}; allowed keys are default, allow, deny, and match`,\n\t\t};\n\t}\n\n\tconst defaultDecision = policy.default === undefined ? \"allow\" : policy.default;\n\tif (defaultDecision !== \"allow\" && defaultDecision !== \"deny\") {\n\t\treturn { ok: false, message: `bash policy default must be \"allow\" or \"deny\"` };\n\t}\n\tconst match = policy.match === undefined ? \"segments\" : policy.match;\n\tif (match !== \"whole\" && match !== \"segments\") {\n\t\treturn { ok: false, message: `bash policy match must be \"whole\" or \"segments\"` };\n\t}\n\tif (policy.allow !== undefined && !Array.isArray(policy.allow)) {\n\t\treturn { ok: false, message: \"bash policy allow must be an array\" };\n\t}\n\tif (policy.deny !== undefined && !Array.isArray(policy.deny)) {\n\t\treturn { ok: false, message: \"bash policy deny must be an array\" };\n\t}\n\n\tconst allow = compileRules(policy.allow, \"allow\");\n\tif (!allow.ok) return allow;\n\tconst deny = compileRules(policy.deny, \"deny\");\n\tif (!deny.ok) return deny;\n\n\treturn {\n\t\tok: true,\n\t\tpolicy: {\n\t\t\tdefaultDecision,\n\t\t\tmatch,\n\t\t\tallow: allow.rules,\n\t\t\tdeny: deny.rules,\n\t\t},\n\t};\n}\n\nexport function validateBashCommandPolicy(policy: BashCommandPolicy): void {\n\tconst compiled = compilePolicy(policy);\n\tif (!compiled.ok) {\n\t\tthrow new Error(`Invalid bash command policy: ${compiled.message}`);\n\t}\n}\n\nexport function invalidPolicyMode(policy: BashCommandPolicy): BashCommandPolicyMatchMode {\n\tif (typeof policy !== \"object\" || policy === null || Array.isArray(policy)) return \"segments\";\n\treturn policy.match === \"whole\" ? \"whole\" : \"segments\";\n}\n"]}
@@ -1,3 +0,0 @@
1
- import type { BashCommandPolicy, BashCommandPolicyDecision } from "./bash-policy-types.ts";
2
- export declare function evaluateBashCommandPolicy(command: string, policy: BashCommandPolicy | undefined): BashCommandPolicyDecision;
3
- //# sourceMappingURL=bash-policy-evaluate.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"bash-policy-evaluate.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash-policy-evaluate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEX,iBAAiB,EACjB,yBAAyB,EAIzB,MAAM,wBAAwB,CAAC;AA0BhC,wBAAgB,yBAAyB,CACxC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,iBAAiB,GAAG,SAAS,GACnC,yBAAyB,CA0E3B","sourcesContent":["import { compilePolicy, invalidPolicyMode } from \"./bash-policy-compile.ts\";\nimport { parseBashCommandSegments, wholeCommandTarget } from \"./bash-policy-parser.ts\";\nimport type {\n\tBashCommandParseResult,\n\tBashCommandPolicy,\n\tBashCommandPolicyDecision,\n\tBashCommandRule,\n\tCompiledBashCommandPolicy,\n\tCompiledRule,\n} from \"./bash-policy-types.ts\";\n\nfunction ruleMatches(rule: CompiledRule, target: string): boolean {\n\tswitch (rule.kind) {\n\t\tcase \"exact\":\n\t\t\treturn target === rule.value;\n\t\tcase \"prefix\":\n\t\t\treturn target.startsWith(rule.value);\n\t\tcase \"glob\":\n\t\t\treturn rule.value.test(target);\n\t\tcase \"regex\":\n\t\t\treturn rule.value.test(target);\n\t}\n}\n\nfunction firstMatchingRule(rules: readonly CompiledRule[], target: string): BashCommandRule | undefined {\n\tfor (const rule of rules) {\n\t\tif (ruleMatches(rule, target)) return rule.source;\n\t}\n\treturn undefined;\n}\n\nfunction isNoRuleDefaultAllowPolicy(policy: CompiledBashCommandPolicy): boolean {\n\treturn policy.defaultDecision === \"allow\" && policy.allow.length === 0 && policy.deny.length === 0;\n}\n\nexport function evaluateBashCommandPolicy(\n\tcommand: string,\n\tpolicy: BashCommandPolicy | undefined,\n): BashCommandPolicyDecision {\n\tif (policy === undefined) {\n\t\treturn { allowed: true, mode: \"segments\", targets: [] };\n\t}\n\n\tconst compiled = compilePolicy(policy);\n\tif (!compiled.ok) {\n\t\treturn {\n\t\t\tallowed: false,\n\t\t\tmode: invalidPolicyMode(policy),\n\t\t\ttargets: [],\n\t\t\trejection: {\n\t\t\t\treason: \"invalid-policy\",\n\t\t\t\tmessage: compiled.message,\n\t\t\t},\n\t\t};\n\t}\n\n\tconst activePolicy = compiled.policy;\n\tif (isNoRuleDefaultAllowPolicy(activePolicy)) {\n\t\treturn { allowed: true, mode: activePolicy.match, targets: [] };\n\t}\n\n\tconst targetsResult: BashCommandParseResult = activePolicy.match === \"whole\"\n\t\t? { ok: true, segments: [wholeCommandTarget(command)] }\n\t\t: parseBashCommandSegments(command);\n\n\tif (!targetsResult.ok) {\n\t\treturn {\n\t\t\tallowed: false,\n\t\t\tmode: activePolicy.match,\n\t\t\ttargets: [],\n\t\t\trejection: {\n\t\t\t\treason: \"unsupported-shell-syntax\",\n\t\t\t\tmessage: targetsResult.error.reason,\n\t\t\t\tparseError: targetsResult.error,\n\t\t\t},\n\t\t};\n\t}\n\n\tfor (const target of targetsResult.segments) {\n\t\tconst denyRule = firstMatchingRule(activePolicy.deny, target.target);\n\t\tif (denyRule !== undefined) {\n\t\t\treturn {\n\t\t\t\tallowed: false,\n\t\t\t\tmode: activePolicy.match,\n\t\t\t\ttargets: targetsResult.segments,\n\t\t\t\trejection: {\n\t\t\t\t\treason: \"matched-deny\",\n\t\t\t\t\tmessage: `command ${JSON.stringify(target.head)} matched a deny rule`,\n\t\t\t\t\ttarget,\n\t\t\t\t\tmatchedRule: denyRule,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst allowRule = firstMatchingRule(activePolicy.allow, target.target);\n\t\tif (allowRule !== undefined || activePolicy.defaultDecision === \"allow\") {\n\t\t\tcontinue;\n\t\t}\n\n\t\treturn {\n\t\t\tallowed: false,\n\t\t\tmode: activePolicy.match,\n\t\t\ttargets: targetsResult.segments,\n\t\t\trejection: {\n\t\t\t\treason: \"default-deny\",\n\t\t\t\tmessage: `command ${JSON.stringify(target.head)} is not permitted by default-deny bash policy`,\n\t\t\t\ttarget,\n\t\t\t},\n\t\t};\n\t}\n\n\treturn { allowed: true, mode: activePolicy.match, targets: targetsResult.segments };\n}\n"]}