@moretea-labs/forge 1.7.0 → 1.7.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.
- package/AGENTS.md +2 -0
- package/CHANGELOG.md +26 -0
- package/CLAUDE.md +2 -0
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/agents/openai.yaml +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +2 -0
- package/assets/plugin-registry.v1.json +3 -3
- package/assets/skill-version.json +2 -2
- package/assets/templates/helpers/check-architecture-sync.sh +17 -0
- package/assets/templates/helpers/inspect-project-state.ts +8 -6
- package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
- package/assets/templates/helpers/workflow-contract.ts +5 -1
- package/docs/architecture/CURRENT.md +12 -4
- package/docs/forge-plugin-management.md +1 -1
- package/docs/operations/controller-performance-and-502.md +19 -0
- package/docs/operations/homebrew.md +2 -2
- package/package.json +5 -2
- package/scripts/architecture-event.ts +2 -820
- package/scripts/architecture-queue.sh +2 -645
- package/scripts/archive-architecture-request.sh +2 -242
- package/scripts/archive-workflow.sh +1 -291
- package/scripts/benchmark-work-efficiency.ts +0 -0
- package/scripts/capability-config.ts +2 -392
- package/scripts/capability-resolver.ts +2 -503
- package/scripts/capture-plan.sh +1 -332
- package/scripts/check-agent-tooling.sh +2 -1531
- package/scripts/check-architecture-sync.sh +2 -302
- package/scripts/check-brain-manifest.sh +2 -253
- package/scripts/check-context-files.sh +2 -47
- package/scripts/check-deploy-sql-order.sh +2 -115
- package/scripts/check-runtime-architecture.mjs +243 -16
- package/scripts/check-skill-version.ts +3 -191
- package/scripts/check-source-duplication.mjs +61 -0
- package/scripts/check-stable-baseline.ts +0 -0
- package/scripts/check-task-sync.sh +2 -63
- package/scripts/check-task-workflow.sh +2 -1000
- package/scripts/codex-handoff-resume.sh +2 -267
- package/scripts/context-contract-sync.sh +2 -514
- package/scripts/contract-run.ts +2 -463
- package/scripts/contract-worktree.sh +1 -702
- package/scripts/ensure-task-workflow.sh +1 -1192
- package/scripts/inspect-project-state.ts +4 -279
- package/scripts/lib/project-init-lib.sh +7 -2
- package/scripts/lib/run-forge-helper.sh +19 -0
- package/scripts/lib/run-forge-helper.ts +26 -0
- package/scripts/migrate-project-template.sh +1 -33
- package/scripts/migrate-workflow-docs.ts +4 -412
- package/scripts/new-plan.sh +1 -180
- package/scripts/new-spec.sh +1 -47
- package/scripts/new-sprint.sh +1 -46
- package/scripts/plan-to-todo.sh +1 -771
- package/scripts/prepare-codex-handoff.sh +2 -201
- package/scripts/prepare-handoff.sh +1 -78
- package/scripts/refresh-current-status.sh +2 -467
- package/scripts/run-governed-gate.ts +3 -0
- package/scripts/select-agent-context-blocks.sh +2 -64
- package/scripts/ship-worktrees.sh +1 -531
- package/scripts/sprint-backlog.sh +1 -834
- package/scripts/stage-runtime-release.ts +2 -1
- package/scripts/summarize-failures.sh +2 -123
- package/scripts/switch-plan.sh +1 -150
- package/scripts/sync-brain-docs.sh +2 -379
- package/scripts/sync-generated-authority.mjs +175 -0
- package/scripts/verify-contract.sh +2 -593
- package/scripts/verify-sprint.sh +1 -500
- package/scripts/workflow-contract.ts +4 -201
- package/scripts/workstream-sync.sh +2 -261
- package/src/cli/agent-jobs/executor-health.ts +1 -1
- package/src/cli/commands/plugin.ts +12 -3
- package/src/cli/controller/check-runner.ts +7 -2
- package/src/cli/controller/context-pack.ts +46 -21
- package/src/cli/github/contracts.ts +16 -0
- package/src/cli/github/github.ts +2 -16
- package/src/cli/github/session.ts +2 -1
- package/src/cli/local-bridge/facade-api.ts +37 -47
- package/src/cli/local-bridge/server.ts +2 -2
- package/src/cli/mcp/legacy-tool-service.ts +10 -33
- package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
- package/src/cli/mcp/repository-tools.ts +125 -101
- package/src/cli/mcp/server.ts +57 -13
- package/src/cli/mcp/tool-contract.ts +14 -0
- package/src/cli/mcp/toolset.ts +2 -2
- package/src/cli/mcp/transports/http.ts +23 -0
- package/src/cli/repositories/command-classifier.ts +2 -2
- package/src/cli/repositories/command-executor.ts +24 -6
- package/src/cli/repositories/repository-snapshot.ts +5 -2
- package/src/cli/repositories/structured-git.ts +57 -3
- package/src/cli/repository/inspector.ts +11 -3
- package/src/runtime/context/generic-lsp-provider.ts +1 -1
- package/src/runtime/context/semantic-navigation-contract.ts +61 -0
- package/src/runtime/context/semantic-navigation.ts +9 -61
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +69 -29
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
- package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
- package/src/runtime/control-plane/execution/work-completion-authority.ts +58 -0
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
- package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
- package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
- package/src/runtime/control-plane/execution/work-handle-authority.ts +150 -0
- package/src/runtime/control-plane/execution/work-handle-store.ts +1 -1
- package/src/runtime/control-plane/execution/work-operation-service.ts +468 -0
- package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
- package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
- package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
- package/src/runtime/control-plane/execution/work-task-receipt.ts +4 -43
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +13 -3
- package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
- package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +5 -1
- package/src/runtime/control-plane/facade/controller-round-relay.ts +164 -37
- package/src/runtime/control-plane/facade/controller-session-store.ts +183 -24
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +15 -1
- package/src/runtime/control-plane/facade/goal-workloop.ts +329 -148
- package/src/runtime/control-plane/facade/index.ts +2 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +90 -22
- package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
- package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
- package/src/runtime/control-plane/facade/types.ts +68 -5
- package/src/runtime/control-plane/facade/work-contract-store.ts +3 -209
- package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +14 -7
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +5 -1
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +13 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +15 -6
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +54 -5
- package/src/runtime/control-plane/launcher/thin-launcher.ts +56 -7
- package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
- package/src/runtime/control-plane/repo-actor/registry.ts +4 -2
- package/src/runtime/control-plane/routing/route-policy.ts +18 -6
- package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
- package/src/runtime/evidence/process-check-execution.ts +18 -0
- package/src/runtime/execution/jobs/child-reference.ts +6 -2
- package/src/runtime/execution/process-runtime/check-result.ts +53 -18
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
- package/src/runtime/execution/process-runtime/command-facade.ts +39 -17
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +20 -3
- package/src/runtime/execution/process-runtime/persisted-check.ts +4 -1
- package/src/runtime/execution/process-runtime/runtime.ts +10 -0
- package/src/runtime/execution/process-runtime/types.ts +2 -12
- package/src/runtime/execution/work-activity.ts +21 -0
- package/src/runtime/execution/workers/executor.ts +18 -13
- package/src/runtime/gateway/mcp/execution-tools.ts +35 -2930
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
- package/src/runtime/gateway/mcp/router.ts +2 -2
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +21 -2
- package/src/runtime/gateway/mcp/runtime-tools.ts +815 -1015
- package/src/runtime/plugins/app-store-connect-adapter.ts +93 -28
- package/src/runtime/plugins/browser-adapter.ts +256 -1083
- package/src/runtime/plugins/browser-handoff-host.ts +2 -25
- package/src/runtime/plugins/browser-handoff.ts +18 -3
- package/src/runtime/plugins/browser-macos-bridge.ts +121 -16
- package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
- package/src/runtime/plugins/browser-native-session-service.ts +222 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +2 -0
- package/src/runtime/plugins/browser-runtime.ts +10 -0
- package/src/runtime/plugins/browser-session-store.ts +121 -0
- package/src/runtime/plugins/browser-session-types.ts +98 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +4 -4
- package/src/runtime/plugins/external-adapter.ts +92 -58
- package/src/runtime/plugins/macos-capability-broker.ts +0 -8
- package/src/runtime/plugins/store.ts +66 -9
- package/src/runtime/plugins/types.ts +3 -1
- package/src/runtime/recovery/maintenance-executor.ts +61 -25
- package/src/runtime/root/package-connector-service.ts +46 -4
- package/src/runtime/root/package-runtime-release.ts +3 -2
- package/src/runtime/root/package-runtime-service.ts +48 -3
- package/src/runtime/root/release-manifest.ts +137 -16
- package/src/runtime/root/release-materialize.ts +109 -35
- package/src/runtime/root/types.ts +10 -0
- package/src/runtime/standalone-recovery/core.ts +425 -194
- package/src/runtime/standalone-recovery/entry.ts +1 -1
- package/src/runtime/standalone-recovery/installer.ts +1 -1
- package/src/runtime/workflow/schedules/engine.ts +113 -87
- package/src/runtime/workflow/schedules/settlement.ts +26 -23
- package/src/runtime/workflow/schedules/store.ts +79 -10
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createPrivateKey, sign } from 'crypto';
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
import { accessSync, constants as fsConstants, existsSync, mkdirSync, promises as fsPromises, readFileSync, statSync, writeFileSync } from 'fs';
|
|
3
3
|
import { dirname, join } from 'path';
|
|
4
4
|
import { CONTROLLER_SCOPE_REPO_ID, controllerSystemRoot } from '../../cli/repositories/controller-home';
|
|
5
5
|
import type {
|
|
@@ -19,6 +19,8 @@ const CONFIG_ROOT = '.forge/plugins';
|
|
|
19
19
|
const LEGACY_CONFIG_ROOT = '.repo-harness/plugins';
|
|
20
20
|
const API_BASE_URL = 'https://api.appstoreconnect.apple.com';
|
|
21
21
|
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
22
|
+
const PRIVATE_KEY_READ_MAX_ATTEMPTS = 4;
|
|
23
|
+
const PRIVATE_KEY_READ_RETRY_BASE_MS = 10;
|
|
22
24
|
|
|
23
25
|
type AppStoreConnectProviderKind = 'mock' | 'app-store-connect-api';
|
|
24
26
|
|
|
@@ -154,20 +156,82 @@ function envValue(name: string): string | undefined {
|
|
|
154
156
|
return stringValue(process.env[name]);
|
|
155
157
|
}
|
|
156
158
|
|
|
157
|
-
|
|
159
|
+
interface AppStoreConnectPrivateKeyReference {
|
|
160
|
+
available: boolean;
|
|
161
|
+
source?: string;
|
|
162
|
+
warning?: string;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function privateKeyPathReference(path: string, source: string): AppStoreConnectPrivateKeyReference {
|
|
166
|
+
if (!existsSync(path)) {
|
|
167
|
+
return { available: false, source, warning: 'Configured App Store Connect private key path does not exist.' };
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
if (!statSync(path).isFile()) {
|
|
171
|
+
return { available: false, source, warning: 'Configured App Store Connect private key path is not a regular file.' };
|
|
172
|
+
}
|
|
173
|
+
accessSync(path, fsConstants.R_OK);
|
|
174
|
+
return { available: true, source };
|
|
175
|
+
} catch {
|
|
176
|
+
// Capability discovery and auth_status are metadata reads. They must never
|
|
177
|
+
// synchronously consume secret bytes just to decide whether a credential
|
|
178
|
+
// reference is usable; filesystem contention (including macOS EDEADLK)
|
|
179
|
+
// therefore degrades readiness instead of escaping as a raw read error.
|
|
180
|
+
return { available: false, source, warning: 'Configured App Store Connect private key path is not readable.' };
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function privateKeyReference(config: AppStoreConnectPluginConfig): AppStoreConnectPrivateKeyReference {
|
|
158
185
|
const inline = envValue('FORGE_ASC_PRIVATE_KEY');
|
|
159
|
-
if (inline) return {
|
|
186
|
+
if (inline) return { available: true, source: 'env:FORGE_ASC_PRIVATE_KEY' };
|
|
160
187
|
const envKeyPath = envValue('FORGE_ASC_PRIVATE_KEY_PATH');
|
|
161
|
-
if (envKeyPath)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
188
|
+
if (envKeyPath) return privateKeyPathReference(envKeyPath, 'env:FORGE_ASC_PRIVATE_KEY_PATH');
|
|
189
|
+
if (config.privateKeyPath) return privateKeyPathReference(config.privateKeyPath, 'config:privateKeyPath');
|
|
190
|
+
return { available: false };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function fsErrorCode(error: unknown): string | undefined {
|
|
194
|
+
if (!error || typeof error !== 'object' || !('code' in error)) return undefined;
|
|
195
|
+
const code = (error as { code?: unknown }).code;
|
|
196
|
+
return typeof code === 'string' ? code : undefined;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function readPrivateKeyFile(path: string, source: string): Promise<{ key?: string; source: string; warning?: string }> {
|
|
200
|
+
for (let attempt = 1; attempt <= PRIVATE_KEY_READ_MAX_ATTEMPTS; attempt += 1) {
|
|
201
|
+
try {
|
|
202
|
+
return { key: await fsPromises.readFile(path, 'utf-8'), source };
|
|
203
|
+
} catch (error) {
|
|
204
|
+
const code = fsErrorCode(error);
|
|
205
|
+
if (code === 'ENOENT') {
|
|
206
|
+
return { source, warning: 'Configured App Store Connect private key path does not exist.' };
|
|
207
|
+
}
|
|
208
|
+
if (code === 'EDEADLK' && attempt < PRIVATE_KEY_READ_MAX_ATTEMPTS) {
|
|
209
|
+
await new Promise((resolve) => setTimeout(resolve, PRIVATE_KEY_READ_RETRY_BASE_MS * attempt));
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
if (code === 'EDEADLK') {
|
|
213
|
+
throw new AssistantPluginError(
|
|
214
|
+
'PLUGIN_PROVIDER_UNAVAILABLE',
|
|
215
|
+
'App Store Connect private key is temporarily unavailable because the filesystem is busy.',
|
|
216
|
+
{ retryable: true, details: { credentialSource: source, code } },
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
throw new AssistantPluginError(
|
|
220
|
+
'PLUGIN_AUTH_REQUIRED',
|
|
221
|
+
'Configured App Store Connect private key could not be read.',
|
|
222
|
+
{ retryable: false, details: { credentialSource: source, code } },
|
|
223
|
+
);
|
|
224
|
+
}
|
|
170
225
|
}
|
|
226
|
+
throw new AssistantPluginError('PLUGIN_PROVIDER_UNAVAILABLE', 'App Store Connect private key is temporarily unavailable.', { retryable: true });
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async function privateKeyMaterial(config: AppStoreConnectPluginConfig): Promise<{ key?: string; source?: string; warning?: string }> {
|
|
230
|
+
const inline = envValue('FORGE_ASC_PRIVATE_KEY');
|
|
231
|
+
if (inline) return { key: inline.replace(/\\n/g, '\n'), source: 'env:FORGE_ASC_PRIVATE_KEY' };
|
|
232
|
+
const envKeyPath = envValue('FORGE_ASC_PRIVATE_KEY_PATH');
|
|
233
|
+
if (envKeyPath) return readPrivateKeyFile(envKeyPath, 'env:FORGE_ASC_PRIVATE_KEY_PATH');
|
|
234
|
+
if (config.privateKeyPath) return readPrivateKeyFile(config.privateKeyPath, 'config:privateKeyPath');
|
|
171
235
|
return {};
|
|
172
236
|
}
|
|
173
237
|
|
|
@@ -188,12 +252,12 @@ function resolveAuth(config: AppStoreConnectPluginConfig): AppStoreConnectAuthSt
|
|
|
188
252
|
|
|
189
253
|
const issuerId = envValue('FORGE_ASC_ISSUER_ID') ?? config.issuerId;
|
|
190
254
|
const keyId = envValue('FORGE_ASC_KEY_ID') ?? config.keyId;
|
|
191
|
-
const key =
|
|
255
|
+
const key = privateKeyReference(config);
|
|
192
256
|
const errors: string[] = [];
|
|
193
257
|
const warnings: string[] = key.warning ? [key.warning] : [];
|
|
194
258
|
if (!issuerId) errors.push('Set FORGE_ASC_ISSUER_ID or configure issuer_id.');
|
|
195
259
|
if (!keyId) errors.push('Set FORGE_ASC_KEY_ID or configure key_id.');
|
|
196
|
-
if (!key.
|
|
260
|
+
if (!key.available) errors.push('Set FORGE_ASC_PRIVATE_KEY, FORGE_ASC_PRIVATE_KEY_PATH, or configure private_key_path.');
|
|
197
261
|
|
|
198
262
|
return {
|
|
199
263
|
provider: 'app-store-connect-api',
|
|
@@ -269,10 +333,10 @@ function derSignatureToJose(signature: Buffer): string {
|
|
|
269
333
|
return Buffer.concat([derIntegerToJose(r), derIntegerToJose(s)]).toString('base64url');
|
|
270
334
|
}
|
|
271
335
|
|
|
272
|
-
function createJwt(config: AppStoreConnectPluginConfig): string {
|
|
336
|
+
async function createJwt(config: AppStoreConnectPluginConfig): Promise<string> {
|
|
273
337
|
const issuerId = envValue('FORGE_ASC_ISSUER_ID') ?? config.issuerId;
|
|
274
338
|
const keyId = envValue('FORGE_ASC_KEY_ID') ?? config.keyId;
|
|
275
|
-
const key = privateKeyMaterial(config).key;
|
|
339
|
+
const key = (await privateKeyMaterial(config)).key;
|
|
276
340
|
if (!issuerId || !keyId || !key) throw new AssistantPluginError('PLUGIN_AUTH_REQUIRED', 'App Store Connect API credentials are incomplete.', { retryable: false });
|
|
277
341
|
|
|
278
342
|
const issuedAt = Math.floor(Date.now() / 1000);
|
|
@@ -298,7 +362,7 @@ async function apiRequest<T>(config: AppStoreConnectPluginConfig, options: {
|
|
|
298
362
|
const response = await fetch(url, {
|
|
299
363
|
method: options.method ?? 'GET',
|
|
300
364
|
headers: {
|
|
301
|
-
authorization: `Bearer ${createJwt(config)}`,
|
|
365
|
+
authorization: `Bearer ${await createJwt(config)}`,
|
|
302
366
|
accept: 'application/json',
|
|
303
367
|
...(options.body === undefined ? {} : { 'content-type': 'application/json' }),
|
|
304
368
|
},
|
|
@@ -807,20 +871,21 @@ export async function executeAppStoreConnectPluginAction(input: AssistantPluginA
|
|
|
807
871
|
|
|
808
872
|
if (config.provider === 'mock') return mockResponse(input.actionId, input.args, config);
|
|
809
873
|
const auth = resolveAuth(config);
|
|
874
|
+
if (input.actionId === 'auth_status') {
|
|
875
|
+
return {
|
|
876
|
+
ready: config.enabled && auth.ready,
|
|
877
|
+
provider: auth.provider,
|
|
878
|
+
issuerId: auth.issuerId ? 'configured' : undefined,
|
|
879
|
+
keyId: auth.keyId ? 'configured' : undefined,
|
|
880
|
+
credentialSource: auth.credentialSource,
|
|
881
|
+
errors: auth.errors,
|
|
882
|
+
warnings: auth.warnings,
|
|
883
|
+
userFacingStatus: userFacingAscStatus(config, auth),
|
|
884
|
+
};
|
|
885
|
+
}
|
|
810
886
|
if (!config.enabled || !auth.ready) throw new AssistantPluginError('PLUGIN_NOT_READY', 'App Store Connect plugin is not ready.', { retryable: false, details: { enabled: config.enabled, errors: auth.errors } });
|
|
811
887
|
|
|
812
888
|
switch (input.actionId) {
|
|
813
|
-
case 'auth_status':
|
|
814
|
-
return {
|
|
815
|
-
ready: auth.ready,
|
|
816
|
-
provider: auth.provider,
|
|
817
|
-
issuerId: auth.issuerId ? 'configured' : undefined,
|
|
818
|
-
keyId: auth.keyId ? 'configured' : undefined,
|
|
819
|
-
credentialSource: auth.credentialSource,
|
|
820
|
-
errors: auth.errors,
|
|
821
|
-
warnings: auth.warnings,
|
|
822
|
-
userFacingStatus: userFacingAscStatus(config, auth),
|
|
823
|
-
};
|
|
824
889
|
case 'list_apps':
|
|
825
890
|
return apiRequest(config, { path: '/v1/apps', query: { 'filter[bundleId]': stringValue(input.args.bundle_id), 'filter[name]': stringValue(input.args.name), limit: limit(input.args.limit) } });
|
|
826
891
|
case 'list_bundle_ids':
|