@planu/cli 5.3.26 → 5.3.28

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 (85) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cli/commands/doctor.d.ts +4 -10
  3. package/dist/cli/commands/doctor.js +5 -31
  4. package/dist/cli/commands/inspect.js +2 -14
  5. package/dist/cli/commands/offline.d.ts +1 -2
  6. package/dist/cli/commands/offline.js +24 -41
  7. package/dist/config/release-policy.json +1 -79
  8. package/dist/engine/auto-updater/release-verifier.js +17 -35
  9. package/dist/engine/core-bridge.d.ts +6 -23
  10. package/dist/engine/core-bridge.js +35 -376
  11. package/dist/engine/execution/job-runtime.d.ts +0 -11
  12. package/dist/engine/execution/job-runtime.js +0 -23
  13. package/dist/engine/handoff-artifacts/io.js +1 -0
  14. package/dist/engine/handoff-artifacts/schemas.d.ts +46 -0
  15. package/dist/engine/handoff-artifacts/schemas.js +15 -0
  16. package/dist/engine/lifecycle-reconciliation.js +6 -5
  17. package/dist/engine/living-spec/annotation-parser.js +0 -14
  18. package/dist/engine/project-graph/native.js +2 -2
  19. package/dist/engine/sandbox/profiles/macos.sb +33 -0
  20. package/dist/engine/scan-project/module-discoverer.js +10 -53
  21. package/dist/engine/scope-boundaries/contradiction-checker.js +1 -1
  22. package/dist/engine/validation/validation-receipt.d.ts +5 -0
  23. package/dist/engine/validation/validation-receipt.js +9 -1
  24. package/dist/engine/validator/deep-code-checker.js +19 -26
  25. package/dist/engine/validator/validation-report-writer.d.ts +1 -0
  26. package/dist/engine/validator/validation-report-writer.js +3 -2
  27. package/dist/security/adapters/macos-keychain.d.ts +5 -3
  28. package/dist/security/adapters/macos-keychain.js +46 -20
  29. package/dist/tools/create-spec/autopilot-analyzer.d.ts +1 -1
  30. package/dist/tools/create-spec/autopilot-analyzer.js +2 -2
  31. package/dist/tools/create-spec/relevance-scorer.d.ts +1 -1
  32. package/dist/tools/create-spec/relevance-scorer.js +1 -17
  33. package/dist/tools/create-spec.js +1 -1
  34. package/dist/tools/update-status/dod-gates.d.ts +1 -0
  35. package/dist/tools/update-status/dod-gates.js +123 -4
  36. package/dist/types/atomic-upgrade.d.ts +0 -2
  37. package/dist/types/core-bridge.d.ts +0 -108
  38. package/dist/types/core-bridge.js +1 -1
  39. package/dist/types/durable-job.d.ts +0 -40
  40. package/dist/types/handoff-artifacts.d.ts +17 -1
  41. package/dist/types/security.d.ts +0 -5
  42. package/dist/types/transition-log.d.ts +1 -1
  43. package/package.json +7 -43
  44. package/planu-plugin.json +1 -1
  45. package/dist/config/native-release-public-key.pem +0 -3
  46. package/dist/config/native-targets.json +0 -87
  47. package/dist/engine/execution/job-executor.d.ts +0 -16
  48. package/dist/engine/execution/job-executor.js +0 -140
  49. package/dist/engine/execution/native-relevance-scorer.d.ts +0 -6
  50. package/dist/engine/execution/native-relevance-scorer.js +0 -64
  51. package/dist/engine/execution/native-worker-dispatch.d.ts +0 -3
  52. package/dist/engine/execution/native-worker-dispatch.js +0 -46
  53. package/dist/engine/execution/native-worker-protocol.d.ts +0 -6
  54. package/dist/engine/execution/native-worker-protocol.js +0 -33
  55. package/dist/engine/execution/native-worker-runtime.d.ts +0 -4
  56. package/dist/engine/execution/native-worker-runtime.js +0 -177
  57. package/dist/engine/execution/native-worker-thread.d.ts +0 -2
  58. package/dist/engine/execution/native-worker-thread.js +0 -66
  59. package/dist/engine/macos-keychain-native-port.d.ts +0 -10
  60. package/dist/engine/macos-keychain-native-port.js +0 -64
  61. package/dist/engine/native-capability-policy.d.ts +0 -4
  62. package/dist/engine/native-capability-policy.js +0 -24
  63. package/dist/engine/native-platform.d.ts +0 -3
  64. package/dist/engine/native-platform.js +0 -44
  65. package/dist/engine/native-runtime-handshake.d.ts +0 -20
  66. package/dist/engine/native-runtime-handshake.js +0 -230
  67. package/dist/engine/planu-core.darwin-arm64.node.manifest.json +0 -39
  68. package/dist/engine/planu-core.darwin-arm64.node.sbom.json +0 -1310
  69. package/dist/engine/planu-core.darwin-x64.node.manifest.json +0 -39
  70. package/dist/engine/planu-core.darwin-x64.node.sbom.json +0 -1310
  71. package/dist/engine/planu-core.linux-arm64-gnu.node.manifest.json +0 -38
  72. package/dist/engine/planu-core.linux-arm64-gnu.node.sbom.json +0 -1310
  73. package/dist/engine/planu-core.linux-arm64-musl.node.manifest.json +0 -38
  74. package/dist/engine/planu-core.linux-arm64-musl.node.sbom.json +0 -1310
  75. package/dist/engine/planu-core.linux-x64-gnu.node.manifest.json +0 -38
  76. package/dist/engine/planu-core.linux-x64-gnu.node.sbom.json +0 -1310
  77. package/dist/engine/planu-core.linux-x64-musl.node.manifest.json +0 -38
  78. package/dist/engine/planu-core.linux-x64-musl.node.sbom.json +0 -1310
  79. package/dist/engine/planu-core.win32-arm64-msvc.node.manifest.json +0 -38
  80. package/dist/engine/planu-core.win32-arm64-msvc.node.sbom.json +0 -1310
  81. package/dist/engine/planu-core.win32-x64-msvc.node.manifest.json +0 -38
  82. package/dist/engine/planu-core.win32-x64-msvc.node.sbom.json +0 -1310
  83. package/dist/types/native-worker.d.ts +0 -48
  84. package/dist/types/native-worker.js +0 -2
  85. package/planu-native.json +0 -82
@@ -1,140 +0,0 @@
1
- import { isNativeWorkerOperation } from './native-worker-protocol.js';
2
- import { runNativeWorkerOperation } from './native-worker-runtime.js';
3
- import { reportClassifiedDegradation } from '../../errors/classified-degradation.js';
4
- const DEFAULT_WORKSPACE_ID = 'global';
5
- const DEFAULT_LEASE_MS = 5_000;
6
- const DEFAULT_HEARTBEAT_MS = 25;
7
- const DEFAULT_MAX_CONCURRENCY = 2;
8
- const MAX_CONCURRENCY = 8;
9
- const RUNNABLE_STATES = new Set(['accepted', 'checkpointed']);
10
- /** Claims and executes allowlisted native jobs while preserving lease and cancellation fencing. */
11
- export class DurableJobExecutor {
12
- runtime;
13
- workerId;
14
- leaseMs;
15
- heartbeatMs;
16
- maxConcurrency;
17
- runNative;
18
- constructor(options) {
19
- requireIdentifier(options.workerId, 'workerId');
20
- this.leaseMs = options.leaseMs ?? DEFAULT_LEASE_MS;
21
- this.heartbeatMs = options.heartbeatMs ?? DEFAULT_HEARTBEAT_MS;
22
- this.maxConcurrency = options.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY;
23
- if (!Number.isSafeInteger(this.leaseMs) || this.leaseMs < 100 || this.leaseMs > 60_000) {
24
- throw new Error('[Planu] executor leaseMs must be an integer between 100 and 60000');
25
- }
26
- if (!Number.isSafeInteger(this.heartbeatMs) ||
27
- this.heartbeatMs < 10 ||
28
- this.heartbeatMs >= this.leaseMs / 2) {
29
- throw new Error('[Planu] executor heartbeatMs must be at least 10 and below half the lease');
30
- }
31
- if (!Number.isSafeInteger(this.maxConcurrency) ||
32
- this.maxConcurrency < 1 ||
33
- this.maxConcurrency > MAX_CONCURRENCY) {
34
- throw new Error(`[Planu] executor maxConcurrency must be between 1 and ${String(MAX_CONCURRENCY)}`);
35
- }
36
- this.runtime = options.runtime;
37
- this.workerId = options.workerId;
38
- this.runNative = options.runNative ?? runNativeWorkerOperation;
39
- }
40
- /** Execute one bounded batch. Jobs not claimed by this worker remain untouched. */
41
- async runOnce(projectId, workspaceId = DEFAULT_WORKSPACE_ID) {
42
- requireIdentifier(projectId, 'projectId');
43
- requireIdentifier(workspaceId, 'workspaceId');
44
- const candidates = this.runtime
45
- .list(projectId, workspaceId)
46
- .filter((job) => RUNNABLE_STATES.has(job.state))
47
- .slice(0, this.maxConcurrency);
48
- return Promise.all(candidates.map((job) => this.execute({
49
- operationId: job.id,
50
- projectId: job.projectId,
51
- workspaceId: job.workspaceId,
52
- })));
53
- }
54
- async execute(identity) {
55
- const claimed = this.runtime.claim({
56
- ...identity,
57
- workerId: this.workerId,
58
- leaseMs: this.leaseMs,
59
- });
60
- if (!claimed) {
61
- return this.runtime.get(identity);
62
- }
63
- const controller = new AbortController();
64
- const heartbeat = setInterval(() => {
65
- const current = this.runtime.get(identity);
66
- if (current.state === 'cancelled' || current.cancelRequested) {
67
- controller.abort(new Error('[Planu] Durable native job cancelled'));
68
- return;
69
- }
70
- if (!this.runtime.renew({
71
- ...identity,
72
- workerId: this.workerId,
73
- fencingToken: claimed.fencingToken,
74
- leaseMs: this.leaseMs,
75
- })) {
76
- controller.abort(new Error('[Planu] Durable native job lease was lost'));
77
- }
78
- }, this.heartbeatMs);
79
- heartbeat.unref();
80
- try {
81
- const payload = parseNativePayload(claimed.payload);
82
- const value = await this.runNative({
83
- projectId: identity.projectId,
84
- operation: payload.operation,
85
- payload: payload.payload,
86
- deadlineAt: payload.deadlineAt,
87
- requestId: identity.operationId,
88
- cancellationId: payload.cancellationId ?? identity.operationId,
89
- signal: controller.signal,
90
- });
91
- return this.runtime.complete({
92
- ...identity,
93
- workerId: this.workerId,
94
- fencingToken: claimed.fencingToken,
95
- result: value,
96
- });
97
- }
98
- catch (error) {
99
- /* reliability-optional: DURABLE_JOB_EXECUTION_FAILURE — runtime records typed failure below */
100
- reportClassifiedDegradation('DURABLE_JOB_EXECUTION_FAILURE', error);
101
- const current = this.runtime.get(identity);
102
- if (current.state === 'cancelled') {
103
- return current;
104
- }
105
- return this.runtime.fail({
106
- ...identity,
107
- workerId: this.workerId,
108
- fencingToken: claimed.fencingToken,
109
- error: boundedError(error),
110
- });
111
- }
112
- finally {
113
- clearInterval(heartbeat);
114
- }
115
- }
116
- }
117
- function parseNativePayload(value) {
118
- if (!value || typeof value !== 'object') {
119
- throw new Error('[Planu] Durable job payload must be an object');
120
- }
121
- const payload = value;
122
- if (payload.kind !== 'native-worker' ||
123
- !isNativeWorkerOperation(payload.operation) ||
124
- !payload.payload ||
125
- typeof payload.payload !== 'object' ||
126
- !Number.isSafeInteger(payload.deadlineAt)) {
127
- throw new Error('[Planu] Durable job payload is not an allowlisted native operation');
128
- }
129
- return payload;
130
- }
131
- function boundedError(error) {
132
- const message = error instanceof Error ? error.message : String(error);
133
- return message.slice(0, 4_096) || 'Native worker failed';
134
- }
135
- function requireIdentifier(value, label) {
136
- if (!value || value.length > 128 || !/^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(value)) {
137
- throw new Error(`[Planu] Invalid ${label}`);
138
- }
139
- }
140
- //# sourceMappingURL=job-executor.js.map
@@ -1,6 +0,0 @@
1
- export declare function findRelevantFilesNative(input: {
2
- rootPath: string;
3
- keywords: readonly string[];
4
- limit: number;
5
- }): string[];
6
- //# sourceMappingURL=native-relevance-scorer.d.ts.map
@@ -1,64 +0,0 @@
1
- import { fastFindFilesByExt, fastReadFiles } from '../core-bridge.js';
2
- // Content scoring is advisory. Keep it bounded so a create_spec acknowledgement
3
- // never turns into an O(project-files) content read on large repositories.
4
- const MAX_CONTENT_CANDIDATES = 2_048;
5
- function normalizedRelativePath(rootPath, filePath) {
6
- const normalizedRoot = rootPath.replaceAll('\\', '/').replace(/\/$/, '');
7
- const normalizedFile = filePath.replaceAll('\\', '/');
8
- return normalizedFile.startsWith(`${normalizedRoot}/`)
9
- ? normalizedFile.slice(normalizedRoot.length + 1)
10
- : normalizedFile;
11
- }
12
- function isRelevantSourcePath(rootPath, filePath) {
13
- const relativePath = normalizedRelativePath(rootPath, filePath);
14
- return ((relativePath.startsWith('src/') || relativePath.startsWith('tests/')) &&
15
- !relativePath.includes('/node_modules/') &&
16
- !relativePath.includes('/dist/'));
17
- }
18
- export function findRelevantFilesNative(input) {
19
- const keywords = [...new Set(input.keywords.map((value) => value.trim()))].filter(Boolean);
20
- if (keywords.length === 0 || !Number.isSafeInteger(input.limit) || input.limit < 1) {
21
- return [];
22
- }
23
- const candidates = fastFindFilesByExt(input.rootPath, ['ts'])
24
- .filter((filePath) => isRelevantSourcePath(input.rootPath, filePath))
25
- .sort((left, right) => left.localeCompare(right));
26
- const scores = new Map();
27
- const pathMatched = new Set();
28
- for (const filePath of candidates) {
29
- const pathTokens = new Set(normalizedRelativePath(input.rootPath, filePath)
30
- .toLowerCase()
31
- .split(/[/\\.\-_]+/)
32
- .filter((token) => token.length >= 3));
33
- const pathScore = keywords.filter((keyword) => pathTokens.has(keyword)).length;
34
- if (pathScore > 0) {
35
- scores.set(filePath, pathScore * 2);
36
- pathMatched.add(filePath);
37
- }
38
- }
39
- // Prefer files already related by path, then inspect a deterministic bounded
40
- // sample for content-only matches. The previous fastFindPatterns call walked
41
- // and opened every source file a second time (10s+ at 100k files).
42
- const contentCandidates = [
43
- ...pathMatched,
44
- ...candidates.filter((filePath) => !pathMatched.has(filePath)),
45
- ].slice(0, MAX_CONTENT_CANDIDATES);
46
- for (const file of fastReadFiles(contentCandidates)) {
47
- const content = file.content.toLowerCase();
48
- let contentScore = 0;
49
- for (const keyword of keywords) {
50
- if (content.includes(keyword.toLowerCase())) {
51
- contentScore += 1;
52
- }
53
- }
54
- if (contentScore > 0) {
55
- scores.set(file.path, (scores.get(file.path) ?? 0) + contentScore);
56
- }
57
- }
58
- return [...scores]
59
- .filter(([, score]) => score > 0)
60
- .sort(([leftPath, leftScore], [rightPath, rightScore]) => rightScore === leftScore ? leftPath.localeCompare(rightPath) : rightScore - leftScore)
61
- .slice(0, input.limit)
62
- .map(([filePath]) => filePath);
63
- }
64
- //# sourceMappingURL=native-relevance-scorer.js.map
@@ -1,3 +0,0 @@
1
- import type { NativeWorkerRequest } from './native-worker-protocol.js';
2
- export declare function dispatchNativeWorkerRequest(request: NativeWorkerRequest): unknown;
3
- //# sourceMappingURL=native-worker-dispatch.d.ts.map
@@ -1,46 +0,0 @@
1
- import { fastDetectDriftParallel, fastScanAndHashFiles, fastScanProjectGraphSources, fastScanProjectMetadata, fastScanSpecAnnotations, fastScanSpecs, } from '../core-bridge.js';
2
- import { findRelevantFilesNative } from './native-relevance-scorer.js';
3
- function requiredString(payload, key) {
4
- const value = payload[key];
5
- if (typeof value !== 'string' || value.trim() === '') {
6
- throw new Error(`[Planu] Native worker payload requires ${key}`);
7
- }
8
- return value;
9
- }
10
- function stringArray(payload, key) {
11
- const value = payload[key];
12
- if (!Array.isArray(value) || !value.every((item) => typeof item === 'string')) {
13
- throw new Error(`[Planu] Native worker payload requires string[] ${key}`);
14
- }
15
- return value;
16
- }
17
- function positiveInteger(payload, key) {
18
- const value = payload[key];
19
- if (!Number.isSafeInteger(value) || value < 1) {
20
- throw new Error(`[Planu] Native worker payload requires positive integer ${key}`);
21
- }
22
- return value;
23
- }
24
- export function dispatchNativeWorkerRequest(request) {
25
- switch (request.operation) {
26
- case 'scan-and-hash-files':
27
- return fastScanAndHashFiles(requiredString(request.payload, 'rootPath'));
28
- case 'scan-project-metadata':
29
- return fastScanProjectMetadata(requiredString(request.payload, 'rootPath'));
30
- case 'scan-spec-annotations':
31
- return fastScanSpecAnnotations(requiredString(request.payload, 'rootPath'));
32
- case 'detect-drift':
33
- return fastDetectDriftParallel(requiredString(request.payload, 'rootPath'), stringArray(request.payload, 'keywords'));
34
- case 'scan-specs':
35
- return fastScanSpecs(requiredString(request.payload, 'rootPath'));
36
- case 'find-relevant-files':
37
- return findRelevantFilesNative({
38
- rootPath: requiredString(request.payload, 'rootPath'),
39
- keywords: stringArray(request.payload, 'keywords'),
40
- limit: positiveInteger(request.payload, 'limit'),
41
- });
42
- case 'scan-project-graph-sources':
43
- return fastScanProjectGraphSources(requiredString(request.payload, 'rootPath'), request.payload.policy, request.payload.overrides ?? {});
44
- }
45
- }
46
- //# sourceMappingURL=native-worker-dispatch.js.map
@@ -1,6 +0,0 @@
1
- import type { NativeWorkerOperation } from '../../types/native-worker.js';
2
- export type { NativeWorkerHandshake, NativeWorkerOperation, NativeWorkerRequest, NativeWorkerResponse, } from '../../types/native-worker.js';
3
- export declare const NATIVE_WORKER_OPERATIONS: readonly ["scan-and-hash-files", "scan-project-metadata", "scan-spec-annotations", "detect-drift", "scan-specs", "find-relevant-files", "scan-project-graph-sources"];
4
- export declare function nativeWorkerSchemaHash(): string;
5
- export declare function isNativeWorkerOperation(value: unknown): value is NativeWorkerOperation;
6
- //# sourceMappingURL=native-worker-protocol.d.ts.map
@@ -1,33 +0,0 @@
1
- import { createHash } from 'node:crypto';
2
- import { NATIVE_CAPABILITY_ABI, NATIVE_PROTOCOL_ABI, RUNTIME_STORAGE_SCHEMA, } from '../native-runtime-handshake.js';
3
- export const NATIVE_WORKER_OPERATIONS = [
4
- 'scan-and-hash-files',
5
- 'scan-project-metadata',
6
- 'scan-spec-annotations',
7
- 'detect-drift',
8
- 'scan-specs',
9
- 'find-relevant-files',
10
- 'scan-project-graph-sources',
11
- ];
12
- export function nativeWorkerSchemaHash() {
13
- return createHash('sha256')
14
- .update(JSON.stringify({
15
- protocolAbi: NATIVE_PROTOCOL_ABI,
16
- capabilityAbi: NATIVE_CAPABILITY_ABI,
17
- storageSchema: RUNTIME_STORAGE_SCHEMA,
18
- capabilities: NATIVE_WORKER_OPERATIONS,
19
- requestFields: [
20
- 'requestId',
21
- 'projectId',
22
- 'deadlineAt',
23
- 'cancellationId',
24
- 'operation',
25
- 'payload',
26
- ],
27
- }))
28
- .digest('hex');
29
- }
30
- export function isNativeWorkerOperation(value) {
31
- return (typeof value === 'string' && NATIVE_WORKER_OPERATIONS.includes(value));
32
- }
33
- //# sourceMappingURL=native-worker-protocol.js.map
@@ -1,4 +0,0 @@
1
- import type { NativeWorkerCall, NativeWorkerFactory } from '../../types/native-worker.js';
2
- export type { NativeWorkerCall } from '../../types/native-worker.js';
3
- export declare function runNativeWorkerOperation(call: NativeWorkerCall, workerFactory?: NativeWorkerFactory): Promise<unknown>;
4
- //# sourceMappingURL=native-worker-runtime.d.ts.map
@@ -1,177 +0,0 @@
1
- import { randomBytes, randomUUID } from 'node:crypto';
2
- import { Worker } from 'node:worker_threads';
3
- import { nativeWorkerSchemaHash, NATIVE_WORKER_OPERATIONS, } from './native-worker-protocol.js';
4
- import { NATIVE_CAPABILITY_ABI, NATIVE_PROTOCOL_ABI, RUNTIME_STORAGE_SCHEMA, } from '../native-runtime-handshake.js';
5
- import { getExecutionContext } from './context.js';
6
- import { redactFailureMessage, reportClassifiedDegradation, } from '../../errors/classified-degradation.js';
7
- function validateCall(call) {
8
- if (call.signal?.aborted) {
9
- throw call.signal.reason instanceof Error
10
- ? call.signal.reason
11
- : new Error('[Planu] Native worker cancelled');
12
- }
13
- if (!call.projectId.trim() || !Number.isSafeInteger(call.deadlineAt)) {
14
- throw new Error('[Planu] Native worker call requires projectId and an absolute deadline');
15
- }
16
- if (!NATIVE_WORKER_OPERATIONS.includes(call.operation)) {
17
- throw new Error('[Planu] Native worker operation is not allowlisted');
18
- }
19
- if (Date.now() >= call.deadlineAt) {
20
- throw new Error('[Planu] Native worker deadline already expired');
21
- }
22
- }
23
- function validateHandshake(handshake, operation) {
24
- const capabilities = new Set(handshake.capabilities);
25
- if (handshake.protocolAbi !== NATIVE_PROTOCOL_ABI ||
26
- handshake.capabilityAbi !== NATIVE_CAPABILITY_ABI ||
27
- handshake.storageSchema !== RUNTIME_STORAGE_SCHEMA ||
28
- handshake.schemaHash !== nativeWorkerSchemaHash() ||
29
- capabilities.size !== NATIVE_WORKER_OPERATIONS.length ||
30
- !NATIVE_WORKER_OPERATIONS.every((capability) => capabilities.has(capability)) ||
31
- !capabilities.has(operation) ||
32
- typeof handshake.engineVersion !== 'string' ||
33
- handshake.engineVersion.trim() === '' ||
34
- typeof handshake.target !== 'string' ||
35
- handshake.target.trim() === '' ||
36
- typeof handshake.artifactHash !== 'string' ||
37
- !/^[a-f0-9]{64}$/.test(handshake.artifactHash) ||
38
- !handshake.compatible) {
39
- throw new Error('[Planu] Native worker handshake is incompatible');
40
- }
41
- }
42
- // eslint-disable-next-line max-lines-per-function -- worker lifecycle keeps cancellation and identity fencing in one closure
43
- export async function runNativeWorkerOperation(call, workerFactory = (url, options) => new Worker(url, {
44
- ...options,
45
- execArgv: process.execArgv.filter((argument) => !argument.startsWith('--input-type')),
46
- })) {
47
- const parent = getExecutionContext();
48
- const deadlineAt = Math.min(call.deadlineAt, parent?.deadlineAt ?? Number.POSITIVE_INFINITY);
49
- const cancellation = linkCancellation(call.signal, parent?.signal);
50
- try {
51
- validateCall({ ...call, deadlineAt, signal: cancellation.signal });
52
- }
53
- catch (error) {
54
- cancellation.dispose();
55
- throw error;
56
- }
57
- const secret = randomBytes(32).toString('hex');
58
- const requestId = call.requestId ?? randomUUID();
59
- const cancellationId = call.cancellationId ?? randomUUID();
60
- const worker = workerFactory(new URL('./native-worker-thread.js', import.meta.url), {
61
- workerData: { secret },
62
- });
63
- return new Promise((resolve, reject) => {
64
- let settled = false;
65
- let requestSent = false;
66
- const timeoutMs = Math.max(1, deadlineAt - Date.now());
67
- const finish = (error, value) => {
68
- if (settled) {
69
- return;
70
- }
71
- settled = true;
72
- clearTimeout(timer);
73
- cancellation.signal.removeEventListener('abort', abort);
74
- cancellation.dispose();
75
- void worker.terminate();
76
- if (error) {
77
- reject(error);
78
- }
79
- else {
80
- resolve(value);
81
- }
82
- };
83
- const abort = () => {
84
- const reason = cancellation.signal.reason;
85
- finish(reason instanceof Error ? reason : new Error('[Planu] Native worker cancelled'));
86
- };
87
- const timer = setTimeout(() => {
88
- finish(new Error('[Planu] Native worker deadline exceeded'));
89
- }, timeoutMs);
90
- cancellation.signal.addEventListener('abort', abort, { once: true });
91
- if (cancellation.signal.aborted) {
92
- abort();
93
- return;
94
- }
95
- worker.once('error', (error) => {
96
- finish(error instanceof Error ? error : new Error(String(error)));
97
- });
98
- worker.once('exit', (code) => {
99
- if (!settled && code !== 0) {
100
- finish(new Error(`[Planu] Native worker exited with code ${String(code)}`));
101
- }
102
- });
103
- worker.on('message', (value) => {
104
- if (!value || typeof value !== 'object' || !('kind' in value)) {
105
- finish(new Error('[Planu] Native worker sent a malformed message'));
106
- return;
107
- }
108
- const message = value;
109
- if (message.kind === 'handshake') {
110
- try {
111
- if (requestSent) {
112
- throw new Error('[Planu] Native worker sent a duplicate handshake');
113
- }
114
- validateHandshake(message, call.operation);
115
- const request = {
116
- kind: 'request',
117
- secret,
118
- requestId,
119
- projectId: call.projectId,
120
- deadlineAt,
121
- cancellationId,
122
- operation: call.operation,
123
- payload: call.payload,
124
- };
125
- requestSent = true;
126
- worker.postMessage(request);
127
- }
128
- catch (error) {
129
- /* reliability-optional: NATIVE_WORKER_MESSAGE_FAILURE — promise rejects through finish */
130
- reportClassifiedDegradation('NATIVE_WORKER_MESSAGE_FAILURE', error);
131
- finish(new Error(redactFailureMessage(error)));
132
- }
133
- return;
134
- }
135
- if (!requestSent || message.requestId !== requestId) {
136
- finish(new Error('[Planu] Native worker response identity mismatch'));
137
- return;
138
- }
139
- if (message.kind === 'error') {
140
- finish(new Error(message.message));
141
- }
142
- else {
143
- finish(undefined, message.value);
144
- }
145
- });
146
- });
147
- }
148
- function linkCancellation(child, parent) {
149
- const controller = new AbortController();
150
- const signals = [child, parent].filter((signal) => signal !== undefined);
151
- const abort = (signal) => {
152
- if (!controller.signal.aborted) {
153
- controller.abort(signal.reason);
154
- }
155
- };
156
- const listeners = signals.map((signal) => {
157
- const listener = () => {
158
- abort(signal);
159
- };
160
- if (signal.aborted) {
161
- abort(signal);
162
- }
163
- else {
164
- signal.addEventListener('abort', listener, { once: true });
165
- }
166
- return { signal, listener };
167
- });
168
- return {
169
- signal: controller.signal,
170
- dispose: () => {
171
- for (const listener of listeners) {
172
- listener.signal.removeEventListener('abort', listener.listener);
173
- }
174
- },
175
- };
176
- }
177
- //# sourceMappingURL=native-worker-runtime.js.map
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=native-worker-thread.d.ts.map
@@ -1,66 +0,0 @@
1
- import { parentPort, workerData } from 'node:worker_threads';
2
- import { isNativeActive, nativeLoadDiagnostic, NATIVE_CAPABILITY_ABI, NATIVE_PROTOCOL_ABI, RUNTIME_STORAGE_SCHEMA, } from '../core-bridge.js';
3
- import { PLANU_VERSION } from '../../config/version.js';
4
- import { redactFailureMessage, reportClassifiedDegradation, } from '../../errors/classified-degradation.js';
5
- import { isNativeWorkerOperation, nativeWorkerSchemaHash, NATIVE_WORKER_OPERATIONS, } from './native-worker-protocol.js';
6
- import { dispatchNativeWorkerRequest } from './native-worker-dispatch.js';
7
- const bootstrap = workerData;
8
- const bootstrapSecret = bootstrap && typeof bootstrap === 'object' && 'secret' in bootstrap
9
- ? bootstrap.secret
10
- : undefined;
11
- if (!parentPort || typeof bootstrapSecret !== 'string' || bootstrapSecret.length < 32) {
12
- throw new Error('[Planu] Native worker bootstrap authentication failed');
13
- }
14
- const port = parentPort;
15
- isNativeActive();
16
- const diagnostic = nativeLoadDiagnostic();
17
- const nativeHandshake = diagnostic.compatibility?.handshake ?? null;
18
- port.postMessage({
19
- kind: 'handshake',
20
- protocolAbi: NATIVE_PROTOCOL_ABI,
21
- capabilityAbi: NATIVE_CAPABILITY_ABI,
22
- storageSchema: RUNTIME_STORAGE_SCHEMA,
23
- schemaHash: nativeWorkerSchemaHash(),
24
- capabilities: NATIVE_WORKER_OPERATIONS,
25
- engineVersion: nativeHandshake?.engineVersion ?? PLANU_VERSION,
26
- target: nativeHandshake?.target ?? null,
27
- artifactHash: diagnostic.artifactSha256 ?? null,
28
- compatible: diagnostic.compatibility?.compatible === true,
29
- });
30
- port.on('message', (value) => {
31
- const request = value;
32
- const requestId = typeof request.requestId === 'string' ? request.requestId : 'invalid';
33
- const respond = (response) => {
34
- port.postMessage(response);
35
- };
36
- try {
37
- if (request.kind !== 'request' ||
38
- request.secret !== bootstrapSecret ||
39
- !isNativeWorkerOperation(request.operation) ||
40
- typeof request.projectId !== 'string' ||
41
- request.projectId.trim() === '' ||
42
- typeof request.cancellationId !== 'string' ||
43
- request.cancellationId.trim() === '' ||
44
- typeof request.deadlineAt !== 'number' ||
45
- Date.now() >= request.deadlineAt ||
46
- !request.payload ||
47
- typeof request.payload !== 'object') {
48
- throw new Error('[Planu] Native worker rejected malformed or expired request');
49
- }
50
- respond({
51
- kind: 'result',
52
- requestId,
53
- value: dispatchNativeWorkerRequest(request),
54
- });
55
- }
56
- catch (error) {
57
- /* reliability-optional: NATIVE_WORKER_OPERATION_FAILURE — typed error response is emitted */
58
- reportClassifiedDegradation('NATIVE_WORKER_OPERATION_FAILURE', error);
59
- respond({
60
- kind: 'error',
61
- requestId,
62
- message: redactFailureMessage(error),
63
- });
64
- }
65
- });
66
- //# sourceMappingURL=native-worker-thread.js.map
@@ -1,10 +0,0 @@
1
- import type { NativeRuntimeCompatibility, PlanuCore } from '../types/core-bridge.js';
2
- import type { MacOSKeychainNativePort } from '../types/security.js';
3
- export declare class ValidationReceiptSignerUnavailableError extends Error {
4
- readonly reason: 'PLATFORM_UNSUPPORTED' | 'NATIVE_DISABLED' | 'ARTIFACT_MISSING' | 'STALE_ARTIFACT' | 'NONCANONICAL_ORIGIN' | 'CAPABILITY_MISSING' | 'PORT_INCOMPLETE' | 'OVERRIDE_REJECTED' | 'PROVIDER_NONCANONICAL' | 'PROVIDER_UNAVAILABLE';
5
- readonly code: 'VALIDATION_RECEIPT_SIGNER_UNAVAILABLE';
6
- constructor(reason: 'PLATFORM_UNSUPPORTED' | 'NATIVE_DISABLED' | 'ARTIFACT_MISSING' | 'STALE_ARTIFACT' | 'NONCANONICAL_ORIGIN' | 'CAPABILITY_MISSING' | 'PORT_INCOMPLETE' | 'OVERRIDE_REJECTED' | 'PROVIDER_NONCANONICAL' | 'PROVIDER_UNAVAILABLE', remediation?: string);
7
- }
8
- /** Build the privileged main-thread port only from a canonical, verified native module. */
9
- export declare function createMacOSKeychainNativePort(platform: NodeJS.Platform, overrideDefined: boolean, disabled: boolean, nativeModule: PlanuCore | null, origin: 'override' | 'local' | 'package' | undefined, capabilities: readonly string[] | undefined, compatibility?: NativeRuntimeCompatibility): MacOSKeychainNativePort;
10
- //# sourceMappingURL=macos-keychain-native-port.d.ts.map
@@ -1,64 +0,0 @@
1
- import { PLANU_VERSION } from '../config/version.js';
2
- const DEFAULT_REMEDIATION = 'Restart the MCP host and retry.';
3
- /** Recover the native target triple from a buildId shaped `<epoch>-<engineVersion>-<triple>`. */
4
- function deriveNativeTargetTriple(buildId, engineVersion) {
5
- const separator = `-${engineVersion}-`;
6
- const index = buildId.indexOf(separator);
7
- if (index === -1) {
8
- return null;
9
- }
10
- const triple = buildId.slice(index + separator.length);
11
- return triple.length > 0 ? triple : null;
12
- }
13
- function staleArtifactRemediation(artifactVersion, buildId) {
14
- const triple = buildId ? deriveNativeTargetTriple(buildId, artifactVersion) : null;
15
- if (!triple) {
16
- return (`Native artifact reports engineVersion ${artifactVersion}, expected ${PLANU_VERSION}. ` +
17
- 'Could not determine the native target triple from the artifact build id; run: ' +
18
- 'bash scripts/build-rust-local.sh --host after manually removing the stale native artifact for your platform.');
19
- }
20
- return (`Native artifact reports engineVersion ${artifactVersion}, expected ${PLANU_VERSION}. Run: ` +
21
- `cargo clean -p planu-core --release --target ${triple} --manifest-path rust/planu-core/Cargo.toml && bash scripts/build-rust-local.sh --host`);
22
- }
23
- export class ValidationReceiptSignerUnavailableError extends Error {
24
- reason;
25
- code = 'VALIDATION_RECEIPT_SIGNER_UNAVAILABLE';
26
- constructor(reason, remediation = DEFAULT_REMEDIATION) {
27
- super(`[Planu] VALIDATION_RECEIPT_SIGNER_UNAVAILABLE:${reason} ${remediation}`);
28
- this.reason = reason;
29
- this.name = 'ValidationReceiptSignerUnavailableError';
30
- }
31
- }
32
- /** Build the privileged main-thread port only from a canonical, verified native module. */
33
- export function createMacOSKeychainNativePort(platform, overrideDefined, disabled, nativeModule, origin, capabilities, compatibility) {
34
- if (platform !== 'darwin') {
35
- throw new ValidationReceiptSignerUnavailableError('PLATFORM_UNSUPPORTED');
36
- }
37
- if (overrideDefined || origin === 'override') {
38
- throw new ValidationReceiptSignerUnavailableError('OVERRIDE_REJECTED');
39
- }
40
- if (disabled) {
41
- throw new ValidationReceiptSignerUnavailableError('NATIVE_DISABLED');
42
- }
43
- if (!nativeModule) {
44
- if (compatibility && !compatibility.compatible) {
45
- const artifactVersion = compatibility.handshake?.engineVersion ?? 'unknown';
46
- throw new ValidationReceiptSignerUnavailableError('STALE_ARTIFACT', staleArtifactRemediation(artifactVersion, compatibility.handshake?.buildId));
47
- }
48
- throw new ValidationReceiptSignerUnavailableError('ARTIFACT_MISSING');
49
- }
50
- if (origin !== 'local' && origin !== 'package') {
51
- throw new ValidationReceiptSignerUnavailableError('NONCANONICAL_ORIGIN');
52
- }
53
- if (!capabilities?.includes('macos-keychain-v1')) {
54
- throw new ValidationReceiptSignerUnavailableError('CAPABILITY_MISSING');
55
- }
56
- const set = nativeModule.macosKeychainSet?.bind(nativeModule);
57
- const get = nativeModule.macosKeychainGet?.bind(nativeModule);
58
- const remove = nativeModule.macosKeychainDelete?.bind(nativeModule);
59
- if (!set || !get || !remove) {
60
- throw new ValidationReceiptSignerUnavailableError('PORT_INCOMPLETE');
61
- }
62
- return { set, get, delete: remove };
63
- }
64
- //# sourceMappingURL=macos-keychain-native-port.js.map
@@ -1,4 +0,0 @@
1
- import type { NativeCapabilityPolicy, NativeFallbackMode } from '../types/core-bridge.js';
2
- export declare const NATIVE_CAPABILITY_POLICIES: readonly NativeCapabilityPolicy[];
3
- export declare function nativeFallbackMode(capability: string): NativeFallbackMode | null;
4
- //# sourceMappingURL=native-capability-policy.d.ts.map