vouchington-tooling 0.29.0 → 0.30.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 (141) hide show
  1. package/README.md +64 -16
  2. package/dist/api-contract-discovery/header-contract-registry.mjs +2 -0
  3. package/dist/api-contract-discovery/protocol-body-status.d.mts +2 -0
  4. package/dist/api-contract-discovery/protocol-body-status.mjs +4 -0
  5. package/dist/api-contract-discovery/protocol-callback-argument-bindings.d.mts +7 -0
  6. package/dist/api-contract-discovery/protocol-callback-argument-bindings.mjs +33 -0
  7. package/dist/api-contract-discovery/protocol-callback-invocation.d.mts +5 -0
  8. package/dist/api-contract-discovery/protocol-callback-invocation.mjs +103 -0
  9. package/dist/api-contract-discovery/protocol-callback-mutations.d.mts +5 -0
  10. package/dist/api-contract-discovery/protocol-callback-mutations.mjs +42 -0
  11. package/dist/api-contract-discovery/protocol-callback-registration.d.mts +3 -0
  12. package/dist/api-contract-discovery/protocol-callback-registration.mjs +27 -0
  13. package/dist/api-contract-discovery/protocol-callback-values.d.mts +17 -0
  14. package/dist/api-contract-discovery/protocol-callback-values.mjs +114 -0
  15. package/dist/api-contract-discovery/protocol-context-alias.d.mts +3 -0
  16. package/dist/api-contract-discovery/protocol-context-alias.mjs +20 -0
  17. package/dist/api-contract-discovery/protocol-contract-extraction.d.mts +9 -0
  18. package/dist/api-contract-discovery/protocol-contract-extraction.mjs +87 -0
  19. package/dist/api-contract-discovery/protocol-contract-keys.d.mts +1 -0
  20. package/dist/api-contract-discovery/protocol-contract-keys.mjs +8 -0
  21. package/dist/api-contract-discovery/protocol-contract-registry.d.mts +5 -0
  22. package/dist/api-contract-discovery/protocol-contract-registry.mjs +133 -0
  23. package/dist/api-contract-discovery/protocol-executable-path.d.mts +3 -0
  24. package/dist/api-contract-discovery/protocol-executable-path.mjs +64 -0
  25. package/dist/api-contract-discovery/protocol-execution-path.d.mts +3 -0
  26. package/dist/api-contract-discovery/protocol-execution-path.mjs +16 -0
  27. package/dist/api-contract-discovery/protocol-http-association.d.mts +7 -0
  28. package/dist/api-contract-discovery/protocol-http-association.mjs +97 -0
  29. package/dist/api-contract-discovery/protocol-http-body-kinds.d.mts +3 -0
  30. package/dist/api-contract-discovery/protocol-http-body-kinds.mjs +19 -0
  31. package/dist/api-contract-discovery/protocol-http-context-callers.d.mts +6 -0
  32. package/dist/api-contract-discovery/protocol-http-context-callers.mjs +34 -0
  33. package/dist/api-contract-discovery/protocol-http-context.d.mts +9 -0
  34. package/dist/api-contract-discovery/protocol-http-context.mjs +108 -0
  35. package/dist/api-contract-discovery/protocol-http-emission.d.mts +3 -0
  36. package/dist/api-contract-discovery/protocol-http-emission.mjs +76 -0
  37. package/dist/api-contract-discovery/protocol-http-implicit-taint.d.mts +7 -0
  38. package/dist/api-contract-discovery/protocol-http-implicit-taint.mjs +26 -0
  39. package/dist/api-contract-discovery/protocol-http-registration.d.mts +12 -0
  40. package/dist/api-contract-discovery/protocol-http-registration.mjs +53 -0
  41. package/dist/api-contract-discovery/protocol-marker-analysis.d.mts +7 -0
  42. package/dist/api-contract-discovery/protocol-marker-analysis.mjs +50 -0
  43. package/dist/api-contract-discovery/protocol-opaque-callback.d.mts +3 -0
  44. package/dist/api-contract-discovery/protocol-opaque-callback.mjs +107 -0
  45. package/dist/api-contract-discovery/protocol-platform-callbacks.d.mts +5 -0
  46. package/dist/api-contract-discovery/protocol-platform-callbacks.mjs +72 -0
  47. package/dist/api-contract-discovery/protocol-platform-mutation-targets.d.mts +5 -0
  48. package/dist/api-contract-discovery/protocol-platform-mutation-targets.mjs +89 -0
  49. package/dist/api-contract-discovery/protocol-platform-writes.d.mts +3 -0
  50. package/dist/api-contract-discovery/protocol-platform-writes.mjs +92 -0
  51. package/dist/api-contract-discovery/protocol-requested-keys.d.mts +5 -0
  52. package/dist/api-contract-discovery/protocol-requested-keys.mjs +45 -0
  53. package/dist/api-contract-discovery/protocol-sse-callers.d.mts +7 -0
  54. package/dist/api-contract-discovery/protocol-sse-callers.mjs +67 -0
  55. package/dist/api-contract-discovery/protocol-sse-emission.d.mts +11 -0
  56. package/dist/api-contract-discovery/protocol-sse-emission.mjs +61 -0
  57. package/dist/api-contract-discovery/protocol-sse-feasible-status.d.mts +3 -0
  58. package/dist/api-contract-discovery/protocol-sse-feasible-status.mjs +24 -0
  59. package/dist/api-contract-discovery/protocol-sse-raw-writes.d.mts +9 -0
  60. package/dist/api-contract-discovery/protocol-sse-raw-writes.mjs +70 -0
  61. package/dist/api-contract-discovery/protocol-sse-status.d.mts +3 -0
  62. package/dist/api-contract-discovery/protocol-sse-status.mjs +84 -0
  63. package/dist/api-contract-discovery/protocol-sse-write-access.d.mts +9 -0
  64. package/dist/api-contract-discovery/protocol-sse-write-access.mjs +42 -0
  65. package/dist/api-contract-discovery/protocol-sse-write-helpers.d.mts +6 -0
  66. package/dist/api-contract-discovery/protocol-sse-write-helpers.mjs +66 -0
  67. package/dist/api-contract-discovery/protocol-sse-write-mutations.d.mts +9 -0
  68. package/dist/api-contract-discovery/protocol-sse-write-mutations.mjs +36 -0
  69. package/dist/api-contract-discovery/protocol-sse-write-resolution.d.mts +14 -0
  70. package/dist/api-contract-discovery/protocol-sse-write-resolution.mjs +79 -0
  71. package/dist/api-contract-discovery/protocol-status-dominance.d.mts +5 -0
  72. package/dist/api-contract-discovery/protocol-status-dominance.mjs +35 -0
  73. package/dist/api-contract-discovery/protocol-write-receiver.d.mts +9 -0
  74. package/dist/api-contract-discovery/protocol-write-receiver.mjs +79 -0
  75. package/dist/api-contract-discovery/query-contract-registry.mjs +2 -0
  76. package/dist/api-contract-discovery/registered-route-binding-writes.d.mts +3 -0
  77. package/dist/api-contract-discovery/registered-route-binding-writes.mjs +30 -0
  78. package/dist/api-contract-discovery/registered-route-catalog.mjs +11 -7
  79. package/dist/api-contract-discovery/registered-route-factory-returns.d.mts +3 -0
  80. package/dist/api-contract-discovery/registered-route-factory-returns.mjs +29 -0
  81. package/dist/api-contract-discovery/registered-route-handler-analysis.d.mts +5 -1
  82. package/dist/api-contract-discovery/registered-route-handler-analysis.mjs +79 -35
  83. package/dist/api-contract-discovery/registered-route-runtime-parameters.d.mts +3 -0
  84. package/dist/api-contract-discovery/registered-route-runtime-parameters.mjs +5 -0
  85. package/dist/api-contract-discovery/registered-route-terminal-error.d.mts +3 -0
  86. package/dist/api-contract-discovery/registered-route-terminal-error.mjs +98 -0
  87. package/dist/api-contract-discovery/request-contract-registry.mjs +2 -0
  88. package/dist/api-contract-discovery/response-contract-implicit.mjs +28 -10
  89. package/dist/api-contract-discovery/response-contract-registry.mjs +14 -1
  90. package/dist/api-contract-discovery/response-contract-route-analysis.d.mts +1 -1
  91. package/dist/api-contract-discovery/response-contract-route-analysis.mjs +22 -4
  92. package/dist/api-contract-discovery/response-contract-status.d.mts +1 -1
  93. package/dist/api-contract-discovery/response-contract-status.mjs +10 -8
  94. package/dist/api-contract-discovery/response-contract-types.d.mts +7 -0
  95. package/dist/cli/index.mjs +0 -3
  96. package/dist/cli/parse-forwarded-commands.d.mts +0 -3
  97. package/dist/cli/parse-forwarded-commands.mjs +0 -2
  98. package/dist/cli/parse.d.mts +0 -3
  99. package/dist/cli/usage.d.mts +1 -1
  100. package/dist/cli/usage.mjs +0 -5
  101. package/dist/contract-schema/contract-schema-bounded-array.d.mts +1 -3
  102. package/dist/contract-schema/contract-schema-bounded-array.mjs +5 -0
  103. package/dist/contract-schema/contract-schema-object-tuple.mjs +7 -5
  104. package/dist/contract-schema/contract-schema-type-extractor.mjs +13 -0
  105. package/dist/contract-schema/contract-schema-validator.mjs +2 -2
  106. package/dist/index.d.mts +0 -2
  107. package/dist/index.mjs +0 -1
  108. package/dist/openapi-document/build-openapi-catalog-sse.d.mts +4 -0
  109. package/dist/openapi-document/build-openapi-catalog-sse.mjs +27 -0
  110. package/dist/openapi-document/build-openapi-document.mjs +5 -2
  111. package/dist/openapi-document/build-openapi-response.d.mts +2 -2
  112. package/dist/openapi-document/build-openapi-response.mjs +33 -3
  113. package/dist/openapi-document/build-openapi-sse-events.d.mts +11 -0
  114. package/dist/openapi-document/build-openapi-sse-events.mjs +46 -0
  115. package/dist/openapi-document/contract-schema-intersection-merge.mjs +11 -11
  116. package/dist/openapi-document/contract-schema-to-openapi.mjs +3 -3
  117. package/dist/openapi-document/openapi-route-helpers.mjs +1 -3
  118. package/dist/openapi-document/openapi-types.d.mts +3 -0
  119. package/dist/openapi-document/operation-types.d.mts +7 -0
  120. package/package.json +3 -8
  121. package/dist/agent-harness-config/apply.d.mts +0 -4
  122. package/dist/agent-harness-config/apply.mjs +0 -72
  123. package/dist/agent-harness-config/cli.d.mts +0 -10
  124. package/dist/agent-harness-config/cli.mjs +0 -100
  125. package/dist/agent-harness-config/index.d.mts +0 -4
  126. package/dist/agent-harness-config/index.mjs +0 -3
  127. package/dist/agent-harness-config/merge-json.d.mts +0 -5
  128. package/dist/agent-harness-config/merge-json.mjs +0 -62
  129. package/dist/agent-harness-config/merge-toml.d.mts +0 -6
  130. package/dist/agent-harness-config/merge-toml.mjs +0 -132
  131. package/dist/agent-harness-config/paths.d.mts +0 -3
  132. package/dist/agent-harness-config/paths.mjs +0 -117
  133. package/dist/agent-harness-config/policy.d.mts +0 -14
  134. package/dist/agent-harness-config/policy.mjs +0 -51
  135. package/dist/agent-harness-config/prerequisites.d.mts +0 -2
  136. package/dist/agent-harness-config/prerequisites.mjs +0 -67
  137. package/dist/agent-harness-config/toml-value.d.mts +0 -6
  138. package/dist/agent-harness-config/toml-value.mjs +0 -69
  139. package/dist/agent-harness-config/types.d.mts +0 -90
  140. package/dist/agent-harness-config/types.mjs +0 -2
  141. package/docs/agent-harness-config.md +0 -134
@@ -42,17 +42,17 @@ function tupleToOpenApi(node, ctx) {
42
42
  }
43
43
  function objectToOpenApi(node, ctx) {
44
44
  const keys = Object.keys(node.properties).toSorted();
45
- const properties = {};
45
+ const properties = new Map();
46
46
  const required = [];
47
47
  for (const key of keys) {
48
48
  const property = node.properties[key];
49
- properties[key] = nodeToOpenApi(property.schema, ctx);
49
+ properties.set(key, nodeToOpenApi(property.schema, ctx));
50
50
  if (property.required)
51
51
  required.push(key);
52
52
  }
53
53
  return {
54
54
  type: 'object',
55
- properties,
55
+ properties: Object.fromEntries(properties),
56
56
  ...(required.length > 0 ? { required } : {}),
57
57
  additionalProperties: node.additionalProperties === false ? false : nodeToOpenApi(node.additionalProperties, ctx),
58
58
  };
@@ -34,11 +34,9 @@ export function catalogResponse(kind, fixedStatus) {
34
34
  };
35
35
  }
36
36
  if (kind === 'error-only') {
37
- const reason = 'registered route has no success response';
38
37
  return {
39
38
  responses: { 405: { $ref: '#/components/responses/Error' } },
40
- unavailable: true,
41
- unavailableReason: reason,
39
+ unavailable: false,
42
40
  };
43
41
  }
44
42
  const reason = kind === 'sse'
@@ -39,6 +39,9 @@ export type OpenApiResponse = {
39
39
  description: string;
40
40
  content?: Record<string, {
41
41
  schema: OpenApiSchema;
42
+ 'x-sse-events'?: Record<string, {
43
+ dataSchema: OpenApiSchema;
44
+ }>;
42
45
  }>;
43
46
  'x-schema-unavailable'?: true;
44
47
  'x-schema-unavailable-reason'?: string;
@@ -1,3 +1,4 @@
1
+ import type { ExtractedResponseContract } from '../contract-schema/types.mts';
1
2
  import type { ContractSchema } from './contract-schema-types.mts';
2
3
  import type { OpenApiQueryContract } from './query-types.mts';
3
4
  export type ResponseContract = {
@@ -11,6 +12,12 @@ export type ResponseContract = {
11
12
  mediaType?: string;
12
13
  mediaTypeKnowledge?: 'known' | 'none' | 'unknown';
13
14
  unavailableReason?: string;
15
+ sseEvents?: readonly {
16
+ eventName: string;
17
+ contract: ExtractedResponseContract;
18
+ }[];
19
+ /** Merge the framework error body into this explicit response's JSON variants. */
20
+ includeDefaultError?: boolean;
14
21
  };
15
22
  export type RequestContract = {
16
23
  source: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vouchington-tooling",
3
- "version": "0.29.0",
3
+ "version": "0.30.0",
4
4
  "description": "Vouchington CLI and extractable tooling libraries.",
5
5
  "homepage": "https://github.com/vouchington/vouchington-tooling/tree/main/packages/vouchington-tooling#readme",
6
6
  "bugs": {
@@ -113,11 +113,6 @@
113
113
  "import": "./dist/ast-grep-pack/index.mjs",
114
114
  "default": "./dist/ast-grep-pack/index.mjs"
115
115
  },
116
- "./agent-harness-config": {
117
- "types": "./dist/agent-harness-config/index.d.mts",
118
- "import": "./dist/agent-harness-config/index.mjs",
119
- "default": "./dist/agent-harness-config/index.mjs"
120
- },
121
116
  "./vitest-blob-manifest": {
122
117
  "types": "./dist/vitest-blob-manifest/index.d.mts",
123
118
  "import": "./dist/vitest-blob-manifest/index.mjs",
@@ -352,7 +347,6 @@
352
347
  "picomatch": "4.0.7",
353
348
  "remark": "15.0.1",
354
349
  "remark-gfm": "4.0.1",
355
- "smol-toml": "1.9.0",
356
350
  "spdx-expression-parse": "5.0.0",
357
351
  "yaml": "2.9.1"
358
352
  },
@@ -361,7 +355,8 @@
361
355
  "@modelcontextprotocol/sdk": "1.31.0",
362
356
  "@types/picomatch": "^4.0.3",
363
357
  "@types/spdx-expression-parse": "4.0.0",
364
- "agent-blackboard": "^0.6.0"
358
+ "agent-blackboard": "^0.6.0",
359
+ "smol-toml": "1.9.0"
365
360
  },
366
361
  "peerDependencies": {
367
362
  "@modelcontextprotocol/sdk": "^1.31.0",
@@ -1,4 +0,0 @@
1
- import type { ApplyTarget, HarnessApplyResult, HarnessCheckResult, HarnessConfigOptions, HarnessPlan } from './types.mts';
2
- export declare function planHarnessConfig(target: ApplyTarget, options?: HarnessConfigOptions): HarnessPlan;
3
- export declare function checkHarnessConfig(target: ApplyTarget, options?: HarnessConfigOptions): Promise<HarnessCheckResult>;
4
- export declare function applyHarnessConfig(target: ApplyTarget, options?: HarnessConfigOptions): Promise<HarnessApplyResult>;
@@ -1,72 +0,0 @@
1
- import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
- import { dirname } from 'node:path';
3
- import { applyJsonPatches } from './merge-json.mjs';
4
- import { applyTomlPatches } from './merge-toml.mjs';
5
- import { planGlobalFiles, planRepoFiles } from './paths.mjs';
6
- import { checkHarnessPrerequisites } from './prerequisites.mjs';
7
- export function planHarnessConfig(target, options) {
8
- const files = target.kind === 'global' ? planGlobalFiles(options) : planRepoFiles(target.root, options);
9
- return { files, target };
10
- }
11
- async function readOptional(path) {
12
- try {
13
- return { exists: true, source: await readFile(path, 'utf8') };
14
- }
15
- catch (error) {
16
- if (error.code === 'ENOENT')
17
- return { exists: false, source: '' };
18
- throw error;
19
- }
20
- }
21
- function patched(plan, source) {
22
- return plan.format === 'json'
23
- ? applyJsonPatches(source, plan.patches, plan.path)
24
- : applyTomlPatches(source, plan.patches, plan.path);
25
- }
26
- function fileResult(plan, exists, drifts) {
27
- if (!exists)
28
- return { action: 'missing', drifts, path: plan.path };
29
- return { action: drifts.length === 0 ? 'ok' : 'update', drifts, path: plan.path };
30
- }
31
- export async function checkHarnessConfig(target, options) {
32
- const plan = planHarnessConfig(target, options);
33
- const files = [];
34
- for (const file of plan.files) {
35
- const { exists, source } = await readOptional(file.path);
36
- files.push(fileResult(file, exists, patched(file, source).drifts));
37
- }
38
- const prerequisites = await checkHarnessPrerequisites(target.kind === 'repo' ? target.root : undefined, options);
39
- return {
40
- compliant: files.every((file) => file.action === 'ok') &&
41
- prerequisites.every((prerequisite) => prerequisite.satisfied),
42
- files,
43
- prerequisites,
44
- target,
45
- };
46
- }
47
- export async function applyHarnessConfig(target, options) {
48
- const plan = planHarnessConfig(target, options);
49
- const files = [];
50
- const written = [];
51
- for (const file of plan.files) {
52
- const { exists, source } = await readOptional(file.path);
53
- const next = patched(file, source);
54
- const result = fileResult(file, exists, next.drifts);
55
- if (result.action === 'ok') {
56
- files.push(result);
57
- continue;
58
- }
59
- await mkdir(dirname(file.path), { recursive: true });
60
- await writeFile(file.path, next.text);
61
- files.push({ ...result, action: exists ? 'update' : 'create' });
62
- written.push(file.path);
63
- }
64
- const prerequisites = await checkHarnessPrerequisites(target.kind === 'repo' ? target.root : undefined, options);
65
- return {
66
- compliant: prerequisites.every((prerequisite) => prerequisite.satisfied),
67
- files,
68
- prerequisites,
69
- target,
70
- written,
71
- };
72
- }
@@ -1,10 +0,0 @@
1
- import { type HarnessId } from './types.mts';
2
- export interface ParsedAgentHarnessConfig {
3
- readonly action: 'dump' | 'check' | 'apply';
4
- readonly harnesses?: readonly HarnessId[];
5
- readonly home?: string;
6
- readonly repos: readonly string[];
7
- readonly global: boolean;
8
- }
9
- export declare function parseAgentHarnessConfigArguments(argv: readonly string[]): ParsedAgentHarnessConfig;
10
- export declare function runAgentHarnessConfigCli(argv: readonly string[]): Promise<number>;
@@ -1,100 +0,0 @@
1
- import { dumpHarnessPolicy } from './policy.mjs';
2
- import { applyHarnessConfig, checkHarnessConfig } from './apply.mjs';
3
- import { HARNESS_IDS } from './types.mjs';
4
- const AGENT_HARNESS_CONFIG_USAGE = 'agent-harness-config dump|check|apply [--global] [--repo PATH]... [--harness NAME]... [--home PATH]';
5
- function isHarness(value) {
6
- return HARNESS_IDS.includes(value);
7
- }
8
- export function parseAgentHarnessConfigArguments(argv) {
9
- const action = argv[0];
10
- if (action !== 'dump' && action !== 'check' && action !== 'apply') {
11
- throw new Error(`unknown agent-harness-config action: ${action ?? ''}\n${AGENT_HARNESS_CONFIG_USAGE}`);
12
- }
13
- let global = false;
14
- let home;
15
- const repos = [];
16
- const harnesses = [];
17
- for (let index = 1; index < argv.length; index += 1) {
18
- const flag = argv[index];
19
- if (flag === '--global') {
20
- global = true;
21
- continue;
22
- }
23
- if (flag === '--repo' || flag === '--harness' || flag === '--home') {
24
- const value = argv[index + 1];
25
- if (value === undefined)
26
- throw new Error(`${flag} requires a value\n${AGENT_HARNESS_CONFIG_USAGE}`);
27
- index += 1;
28
- if (flag === '--repo')
29
- repos.push(value);
30
- else if (flag === '--home')
31
- home = value;
32
- else if (!isHarness(value))
33
- throw new Error(`unknown harness: ${value}`);
34
- else
35
- harnesses.push(value);
36
- continue;
37
- }
38
- throw new Error(`unknown agent-harness-config option: ${flag}\n${AGENT_HARNESS_CONFIG_USAGE}`);
39
- }
40
- if (action !== 'dump' && !global && repos.length === 0) {
41
- throw new Error(`check and apply require --global and/or --repo\n${AGENT_HARNESS_CONFIG_USAGE}`);
42
- }
43
- return {
44
- action,
45
- global,
46
- repos,
47
- ...(harnesses.length > 0 ? { harnesses } : {}),
48
- ...(home === undefined ? {} : { home }),
49
- };
50
- }
51
- function formatDrift(drift) {
52
- return ` ${drift.path} ${drift.key}: ${JSON.stringify(drift.current)} -> ${JSON.stringify(drift.desired)}`;
53
- }
54
- function targets(parsed) {
55
- const list = [];
56
- if (parsed.global)
57
- list.push({ kind: 'global' });
58
- for (const root of parsed.repos)
59
- list.push({ kind: 'repo', root });
60
- return list;
61
- }
62
- export async function runAgentHarnessConfigCli(argv) {
63
- try {
64
- const parsed = parseAgentHarnessConfigArguments(argv);
65
- if (parsed.action === 'dump') {
66
- process.stdout.write(`${JSON.stringify(dumpHarnessPolicy(), null, 2)}\n`);
67
- return 0;
68
- }
69
- const options = {
70
- ...(parsed.harnesses === undefined ? {} : { harnesses: parsed.harnesses }),
71
- ...(parsed.home === undefined ? {} : { home: parsed.home }),
72
- };
73
- let compliant = true;
74
- for (const target of targets(parsed)) {
75
- const result = parsed.action === 'apply'
76
- ? await applyHarnessConfig(target, options)
77
- : await checkHarnessConfig(target, options);
78
- compliant &&=
79
- parsed.action === 'apply'
80
- ? result.prerequisites.every((prerequisite) => prerequisite.satisfied)
81
- : result.compliant;
82
- for (const file of result.files) {
83
- if (file.action === 'ok')
84
- process.stdout.write(`ok ${file.path}\n`);
85
- else
86
- process.stdout.write(`${file.action} ${file.path}\n`);
87
- for (const drift of file.drifts)
88
- process.stdout.write(`${formatDrift(drift)}\n`);
89
- }
90
- for (const prerequisite of result.prerequisites) {
91
- process.stdout.write(`prerequisite ${prerequisite.harness} ${prerequisite.key} satisfied=${prerequisite.satisfied}: ${prerequisite.message}\n`);
92
- }
93
- }
94
- return compliant ? 0 : 1;
95
- }
96
- catch (error) {
97
- process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
98
- return 2;
99
- }
100
- }
@@ -1,4 +0,0 @@
1
- export { applyHarnessConfig, checkHarnessConfig, planHarnessConfig } from './apply.mts';
2
- export { dumpHarnessPolicy, DEFAULT_EXTRA_WRITABLE_ROOTS } from './policy.mts';
3
- export { HARNESS_IDS } from './types.mts';
4
- export type { ApplyTarget, FilePlan, FileResult, HarnessApplyResult, HarnessCheckResult, HarnessConfigOptions, HarnessId, HarnessPlan, HarnessPolicyDump, JsonPatch, KeyDrift, TomlPatch, TomlValue, } from './types.mts';
@@ -1,3 +0,0 @@
1
- export { applyHarnessConfig, checkHarnessConfig, planHarnessConfig } from './apply.mjs';
2
- export { dumpHarnessPolicy, DEFAULT_EXTRA_WRITABLE_ROOTS } from './policy.mjs';
3
- export { HARNESS_IDS } from './types.mjs';
@@ -1,5 +0,0 @@
1
- import type { JsonPatch, KeyDrift } from './types.mts';
2
- export declare function applyJsonPatches(source: string, patches: readonly JsonPatch[], path: string): {
3
- drifts: KeyDrift[];
4
- text: string;
5
- };
@@ -1,62 +0,0 @@
1
- import { sameValue } from './policy.mjs';
2
- function getJsonPath(value, path) {
3
- let current = value;
4
- for (const key of path) {
5
- if (current === null || typeof current !== 'object' || Array.isArray(current))
6
- return undefined;
7
- current = current[key];
8
- }
9
- return current;
10
- }
11
- function setJsonPath(target, path, value) {
12
- let current = target;
13
- for (const key of path.slice(0, -1)) {
14
- const next = current[key];
15
- if (next === null || typeof next !== 'object' || Array.isArray(next)) {
16
- current[key] = {};
17
- }
18
- current = current[key];
19
- }
20
- const last = path.at(-1);
21
- if (last !== undefined)
22
- current[last] = value;
23
- }
24
- function parseJsonObject(source, path) {
25
- if (source.trim() === '')
26
- return {};
27
- const parsed = JSON.parse(source);
28
- if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
29
- throw new Error(`${path}: expected a JSON object`);
30
- }
31
- return parsed;
32
- }
33
- function unionMerge(current, values) {
34
- const base = Array.isArray(current) ? current : [];
35
- const merged = [...base];
36
- for (const value of values) {
37
- if (!merged.some((entry) => sameValue(entry, value)))
38
- merged.push(value);
39
- }
40
- return merged;
41
- }
42
- function desiredValue(document, patch) {
43
- if (patch.merge !== 'union')
44
- return patch.value;
45
- return unionMerge(getJsonPath(document, patch.path), patch.value);
46
- }
47
- export function applyJsonPatches(source, patches, path) {
48
- const document = parseJsonObject(source, path);
49
- const resolved = patches.map((patch) => ({ desired: desiredValue(document, patch), patch }));
50
- const drifts = [];
51
- for (const { desired, patch } of resolved) {
52
- const current = getJsonPath(document, patch.path);
53
- if (!sameValue(current, desired)) {
54
- drifts.push({ current, desired, key: patch.path.join('.'), path });
55
- }
56
- }
57
- if (drifts.length === 0)
58
- return { drifts, text: source === '' ? '{}\n' : source };
59
- for (const { desired, patch } of resolved)
60
- setJsonPath(document, patch.path, desired);
61
- return { drifts, text: `${JSON.stringify(document, null, 2)}\n` };
62
- }
@@ -1,6 +0,0 @@
1
- import type { KeyDrift, TomlPatch } from './types.mts';
2
- export declare function readTomlKey(text: string, table: string, key: string): unknown;
3
- export declare function applyTomlPatches(source: string, patches: readonly TomlPatch[], path: string): {
4
- drifts: KeyDrift[];
5
- text: string;
6
- };
@@ -1,132 +0,0 @@
1
- import { parse as parseToml } from 'smol-toml';
2
- import { sameValue } from './policy.mjs';
3
- import { formatTomlValue, parseTomlValue } from './toml-value.mjs';
4
- function lineStarts(text) {
5
- const starts = [0];
6
- for (let index = 0; index < text.length; index += 1) {
7
- if (text[index] === '\n')
8
- starts.push(index + 1);
9
- }
10
- return starts;
11
- }
12
- function lineAt(text, start) {
13
- const end = text.indexOf('\n', start);
14
- return text.slice(start, end === -1 ? text.length : end);
15
- }
16
- function isHeaderLine(line) {
17
- return line.trimStart().startsWith('[');
18
- }
19
- function headerName(line) {
20
- const trimmed = line.trim();
21
- if (trimmed.startsWith('[[') || !trimmed.startsWith('['))
22
- return undefined;
23
- const close = trimmed.indexOf(']');
24
- if (close <= 1)
25
- return undefined;
26
- return trimmed.slice(1, close);
27
- }
28
- function tableRange(text, table) {
29
- const starts = lineStarts(text);
30
- if (table === '') {
31
- const first = starts.find((start) => isHeaderLine(lineAt(text, start)));
32
- return { bodyEnd: first ?? text.length, bodyStart: 0 };
33
- }
34
- for (const start of starts) {
35
- const line = lineAt(text, start);
36
- if (headerName(line) !== table)
37
- continue;
38
- const bodyStart = start + line.length + (text[start + line.length] === '\n' ? 1 : 0);
39
- const next = starts.find((candidate) => candidate >= bodyStart && isHeaderLine(lineAt(text, candidate)));
40
- return { bodyEnd: next ?? text.length, bodyStart };
41
- }
42
- return { bodyEnd: -1, bodyStart: -1 };
43
- }
44
- function keyMatch(text, bodyStart, bodyEnd, key) {
45
- const pattern = new RegExp(`^[ \\t]*${key.replaceAll(/[.*+?^${}()|[\]\\]/g, '\\$&')}[ \\t]*=[ \\t]*`, 'm');
46
- const slice = text.slice(bodyStart, bodyEnd);
47
- const match = pattern.exec(slice);
48
- if (!match || match.index === undefined)
49
- return undefined;
50
- const equals = bodyStart + match.index + match[0].length;
51
- return { equals, parsed: parseTomlValue(text, equals) };
52
- }
53
- export function readTomlKey(text, table, key) {
54
- const range = tableRange(text, table);
55
- if (range.bodyStart < 0)
56
- return undefined;
57
- return keyMatch(text, range.bodyStart, range.bodyEnd, key)?.parsed.value;
58
- }
59
- function appendTable(text, table, assignment) {
60
- const prefix = text === '' || text.endsWith('\n') ? text : `${text}\n`;
61
- const blank = prefix !== '' && !prefix.endsWith('\n\n') ? '\n' : '';
62
- return `${prefix}${blank}[${table}]\n${assignment}\n`;
63
- }
64
- function upsertTomlKey(text, table, key, value) {
65
- const assignment = `${key} = ${formatTomlValue(value)}`;
66
- const range = tableRange(text, table);
67
- if (range.bodyStart < 0)
68
- return appendTable(text, table, assignment);
69
- const existing = keyMatch(text, range.bodyStart, range.bodyEnd, key);
70
- if (existing) {
71
- return `${text.slice(0, existing.equals)}${formatTomlValue(value)}${text.slice(existing.parsed.end)}`;
72
- }
73
- const at = range.bodyEnd;
74
- const prefix = at > 0 && text[at - 1] !== '\n' ? '\n' : '';
75
- return `${text.slice(0, at)}${prefix}${assignment}\n${text.slice(at)}`;
76
- }
77
- function tomlPatchValue(parsed, table, key) {
78
- let current = parsed;
79
- if (table !== '') {
80
- for (const segment of table.split('.')) {
81
- /* v8 ignore next 2 -- if `text` parsed, every ancestor of a dotted table header is
82
- guaranteed to be a table too, so a real TOML parser can never hand back a non-object
83
- mid-path here. */
84
- if (current === null || typeof current !== 'object' || Array.isArray(current))
85
- return undefined;
86
- current = current[segment];
87
- }
88
- }
89
- /* v8 ignore next 2 -- assertValidToml only reaches a patch's table here once readTomlKey has
90
- already proven (as a match, or by forcing the write that creates it) that the table is a
91
- real nested object in this parsed document. */
92
- if (current === null || typeof current !== 'object' || Array.isArray(current))
93
- return undefined;
94
- return current[key];
95
- }
96
- function assertValidToml(text, patches, path) {
97
- let parsed;
98
- try {
99
- parsed = parseToml(text);
100
- }
101
- catch (error) {
102
- /* v8 ignore next -- smol-toml's parse() only ever throws TomlError, which extends Error, so
103
- the non-Error fallback here is unreachable in practice. */
104
- const message = error instanceof Error ? error.message : String(error);
105
- throw new Error(`${path}: produced invalid TOML (${message})`);
106
- }
107
- for (const patch of patches) {
108
- const value = tomlPatchValue(parsed, patch.table, patch.key);
109
- /* v8 ignore next 6 -- backstop for a formatTomlValue/parseToml encoding mismatch; formatTomlValue's
110
- JSON.stringify-based escaping is a valid TOML basic string in every case this module writes,
111
- so a real parser always reads back what we just wrote. */
112
- if (!sameValue(value, patch.value)) {
113
- const key = patch.table === '' ? patch.key : `${patch.table}.${patch.key}`;
114
- throw new Error(`${path}: wrote ${key} but a real TOML parser reads back ${JSON.stringify(value)}`);
115
- }
116
- }
117
- }
118
- export function applyTomlPatches(source, patches, path) {
119
- const drifts = [];
120
- let text = source.replaceAll('\r\n', '\n');
121
- for (const patch of patches) {
122
- const current = readTomlKey(text, patch.table, patch.key);
123
- if (sameValue(current, patch.value))
124
- continue;
125
- const key = patch.table === '' ? patch.key : `${patch.table}.${patch.key}`;
126
- drifts.push({ current, desired: patch.value, key, path });
127
- text = upsertTomlKey(text, patch.table, patch.key, patch.value);
128
- }
129
- if (drifts.length > 0)
130
- assertValidToml(text, patches, path);
131
- return { drifts, text };
132
- }
@@ -1,3 +0,0 @@
1
- import { type FilePlan, type HarnessConfigOptions } from './types.mts';
2
- export declare function planGlobalFiles(options?: HarnessConfigOptions): FilePlan[];
3
- export declare function planRepoFiles(root: string, options?: HarnessConfigOptions): FilePlan[];
@@ -1,117 +0,0 @@
1
- import { homedir } from 'node:os';
2
- import { join, resolve } from 'node:path';
3
- import { CLAUDE_DEFAULT_MODE, CODEX_APPROVAL_POLICY, CODEX_APPROVALS_REVIEWER, CODEX_SANDBOX_MODE, CURSOR_APPROVAL_MODE, CURSOR_SANDBOX_MODE, DEFAULT_EXTRA_WRITABLE_ROOTS, GROK_AUTO_MODE_ENABLED, GROK_DEFAULT_AUTO_MODE, GROK_PERMISSION_MODE, } from './policy.mjs';
4
- import { HARNESS_IDS } from './types.mjs';
5
- function selected(options) {
6
- return new Set(options?.harnesses ?? HARNESS_IDS);
7
- }
8
- function extraRoots(options) {
9
- return options?.extraWritableRoots ?? DEFAULT_EXTRA_WRITABLE_ROOTS;
10
- }
11
- function wants(set, id) {
12
- return set.has(id);
13
- }
14
- function codexPatches(roots) {
15
- return {
16
- format: 'toml',
17
- patches: [
18
- { key: 'approval_policy', table: '', value: CODEX_APPROVAL_POLICY },
19
- { key: 'approvals_reviewer', table: '', value: CODEX_APPROVALS_REVIEWER },
20
- { key: 'sandbox_mode', table: '', value: CODEX_SANDBOX_MODE },
21
- { key: 'network_access', table: 'sandbox_workspace_write', value: true },
22
- { key: 'writable_roots', table: 'sandbox_workspace_write', value: roots },
23
- ],
24
- path: '',
25
- };
26
- }
27
- function grokSandboxPatches(roots) {
28
- return {
29
- format: 'toml',
30
- patches: [
31
- { key: 'extends', table: 'profiles.workspace-write', value: 'workspace' },
32
- { key: 'read_write', table: 'profiles.workspace-write', value: roots },
33
- ],
34
- path: '',
35
- };
36
- }
37
- function homeDir(options) {
38
- return resolve(options?.home ?? homedir());
39
- }
40
- export function planGlobalFiles(options) {
41
- const harnesses = selected(options);
42
- const roots = extraRoots(options);
43
- const home = homeDir(options);
44
- const files = [];
45
- if (wants(harnesses, 'claude')) {
46
- files.push({
47
- format: 'json',
48
- patches: [
49
- { path: ['permissions', 'defaultMode'], value: CLAUDE_DEFAULT_MODE },
50
- { path: ['sandbox', 'enabled'], value: true },
51
- { path: ['sandbox', 'failIfUnavailable'], value: true },
52
- { merge: 'union', path: ['sandbox', 'filesystem', 'allowWrite'], value: roots },
53
- { path: ['useAutoModeDuringPlan'], value: true },
54
- ],
55
- path: join(home, '.claude', 'settings.json'),
56
- });
57
- }
58
- if (wants(harnesses, 'codex'))
59
- files.push({ ...codexPatches(roots), path: join(home, '.codex', 'config.toml') });
60
- if (wants(harnesses, 'grok')) {
61
- files.push({ ...grokSandboxPatches(roots), path: join(home, '.grok', 'sandbox.toml') });
62
- files.push({
63
- format: 'toml',
64
- patches: [
65
- { key: 'permission_mode', table: 'ui', value: GROK_PERMISSION_MODE },
66
- { key: 'enabled', table: 'auto_mode', value: GROK_AUTO_MODE_ENABLED },
67
- { key: 'default_auto_mode', table: '', value: GROK_DEFAULT_AUTO_MODE },
68
- ],
69
- path: join(home, '.grok', 'config.toml'),
70
- });
71
- }
72
- if (wants(harnesses, 'cursor')) {
73
- files.push({
74
- format: 'json',
75
- patches: [
76
- { path: ['approvalMode'], value: CURSOR_APPROVAL_MODE },
77
- { path: ['sandbox', 'mode'], value: CURSOR_SANDBOX_MODE },
78
- ],
79
- path: join(home, '.cursor', 'cli-config.json'),
80
- });
81
- }
82
- return files;
83
- }
84
- export function planRepoFiles(root, options) {
85
- const harnesses = selected(options);
86
- const roots = extraRoots(options);
87
- const repo = resolve(root);
88
- const files = [];
89
- if (wants(harnesses, 'claude')) {
90
- files.push({
91
- format: 'json',
92
- patches: [
93
- { path: ['sandbox', 'enabled'], value: true },
94
- { path: ['sandbox', 'failIfUnavailable'], value: true },
95
- { merge: 'union', path: ['sandbox', 'filesystem', 'allowWrite'], value: roots },
96
- ],
97
- path: join(repo, '.claude', 'settings.json'),
98
- });
99
- }
100
- if (wants(harnesses, 'codex'))
101
- files.push({ ...codexPatches(roots), path: join(repo, '.codex', 'config.toml') });
102
- if (wants(harnesses, 'grok'))
103
- files.push({ ...grokSandboxPatches(roots), path: join(repo, '.grok', 'sandbox.toml') });
104
- if (wants(harnesses, 'cursor')) {
105
- files.push({
106
- format: 'json',
107
- patches: [
108
- { path: ['type'], value: 'workspace_readwrite' },
109
- { path: ['enableSharedBuildCache'], value: true },
110
- { path: ['networkPolicy', 'default'], value: 'allow' },
111
- { path: ['additionalReadwritePaths'], value: roots },
112
- ],
113
- path: join(repo, '.cursor', 'sandbox.json'),
114
- });
115
- }
116
- return files;
117
- }
@@ -1,14 +0,0 @@
1
- import type { HarnessPolicyDump } from './types.mts';
2
- export declare const DEFAULT_EXTRA_WRITABLE_ROOTS: readonly ['~/.cargo', '~/Library/pnpm', '~/.local/share/pnpm', '~/.pnpm-store', '~/Library/Caches/no-mistakes', '~/Library/Caches/pnpm', '~/.pnpm-state', '~/.cache/no-mistakes', '~/.cache/pnpm', '~/.local/state/pnpm', '/var/folders', '/private/var/folders'];
3
- export declare const CLAUDE_DEFAULT_MODE = "auto";
4
- export declare const CODEX_APPROVAL_POLICY = "on-request";
5
- export declare const CODEX_APPROVALS_REVIEWER = "auto_review";
6
- export declare const CODEX_SANDBOX_MODE = "workspace-write";
7
- export declare const GROK_PERMISSION_MODE = "auto";
8
- export declare const GROK_SANDBOX_PROFILE = "workspace-write";
9
- export declare const GROK_AUTO_MODE_ENABLED = true;
10
- export declare const GROK_DEFAULT_AUTO_MODE = true;
11
- export declare const CURSOR_APPROVAL_MODE = "auto-review";
12
- export declare const CURSOR_SANDBOX_MODE = "enabled";
13
- export declare function dumpHarnessPolicy(extraWritableRoots?: readonly string[]): HarnessPolicyDump;
14
- export declare function sameValue(left: unknown, right: unknown): boolean;