sneakoscope 2.0.4 → 2.0.6
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/README.md +18 -11
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/.sks-build-stamp.json +4 -4
- package/dist/bin/sks.js +1 -1
- package/dist/build-manifest.json +78 -8
- package/dist/cli/install-helpers.js +23 -0
- package/dist/commands/codex-app.js +25 -3
- package/dist/commands/doctor.js +33 -4
- package/dist/commands/mad-sks.js +2 -2
- package/dist/core/agents/agent-orchestrator.js +22 -3
- package/dist/core/agents/agent-proof-evidence.js +59 -2
- package/dist/core/agents/agent-roster.js +35 -6
- package/dist/core/agents/agent-schema.js +1 -1
- package/dist/core/agents/agent-worker-pipeline.js +9 -1
- package/dist/core/agents/native-worker-backend-router.js +50 -10
- package/dist/core/agents/ollama-worker-config.js +164 -15
- package/dist/core/codex/codex-0-137-compat.js +119 -0
- package/dist/core/codex-app.js +124 -2
- package/dist/core/codex-control/codex-control-proof.js +4 -1
- package/dist/core/codex-control/codex-sdk-capability.js +1 -1
- package/dist/core/codex-control/codex-task-runner.js +329 -5
- package/dist/core/codex-control/python-codex-sdk-adapter.js +197 -0
- package/dist/core/codex-control/python-codex-sdk-event-translator.js +14 -0
- package/dist/core/commands/local-model-command.js +65 -19
- package/dist/core/commands/naruto-command.js +124 -8
- package/dist/core/commands/run-command.js +1 -1
- package/dist/core/doctor/doctor-readiness-matrix.js +21 -2
- package/dist/core/fsx.js +1 -1
- package/dist/core/hooks-runtime.js +2 -233
- package/dist/core/init.js +8 -8
- package/dist/core/local-llm/local-llm-backpressure.js +20 -0
- package/dist/core/local-llm/local-llm-capability.js +29 -0
- package/dist/core/local-llm/local-llm-client.js +100 -0
- package/dist/core/local-llm/local-llm-config.js +6 -1
- package/dist/core/local-llm/local-llm-context-cache.js +21 -0
- package/dist/core/local-llm/local-llm-control-adapter.js +101 -0
- package/dist/core/local-llm/local-llm-json-repair.js +52 -0
- package/dist/core/local-llm/local-llm-metrics.js +42 -0
- package/dist/core/local-llm/local-llm-ollama-client.js +67 -0
- package/dist/core/local-llm/local-llm-openai-compatible-client.js +30 -0
- package/dist/core/local-llm/local-llm-prompt-cache.js +12 -0
- package/dist/core/local-llm/local-llm-scheduler.js +29 -0
- package/dist/core/local-llm/local-llm-schema-enforcer.js +15 -0
- package/dist/core/local-llm/local-llm-smoke.js +83 -0
- package/dist/core/local-llm/local-llm-warmup.js +20 -0
- package/dist/core/local-llm/local-worker-eligibility.js +27 -0
- package/dist/core/naruto/hardware-capacity-probe.js +36 -0
- package/dist/core/naruto/naruto-active-pool.js +134 -0
- package/dist/core/naruto/naruto-backpressure.js +13 -0
- package/dist/core/naruto/naruto-concurrency-governor.js +65 -0
- package/dist/core/naruto/naruto-finalizer.js +18 -0
- package/dist/core/naruto/naruto-generation-scheduler.js +18 -0
- package/dist/core/naruto/naruto-gpt-final-pack.js +49 -0
- package/dist/core/naruto/naruto-parallel-patch-apply.js +95 -0
- package/dist/core/naruto/naruto-patch-transaction-batch.js +42 -0
- package/dist/core/naruto/naruto-role-policy.js +107 -0
- package/dist/core/naruto/naruto-verification-dag.js +42 -0
- package/dist/core/naruto/naruto-verification-pool.js +18 -0
- package/dist/core/naruto/naruto-work-graph.js +198 -0
- package/dist/core/naruto/naruto-work-item.js +40 -0
- package/dist/core/naruto/naruto-work-stealing.js +11 -0
- package/dist/core/naruto/resource-pressure-monitor.js +32 -0
- package/dist/core/pipeline/finalize-pipeline-result.js +58 -0
- package/dist/core/pipeline/gpt-final-required.js +12 -0
- package/dist/core/pipeline-internals/runtime-core.js +1 -1
- package/dist/core/ppt.js +31 -8
- package/dist/core/product-design-app-server.js +410 -0
- package/dist/core/product-design-plugin.js +139 -0
- package/dist/core/prompt/prompt-placeholder-guard.js +30 -0
- package/dist/core/router/capability-card.js +13 -0
- package/dist/core/router/route-cache.js +3 -0
- package/dist/core/router/ultra-router.js +2 -1
- package/dist/core/routes.js +12 -12
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-lane-runtime.js +2 -2
- package/dist/core/zellij/zellij-naruto-dashboard.js +36 -0
- package/dist/core/zellij/zellij-worker-pane-manager.js +4 -4
- package/dist/scripts/blackbox-command-import-smoke.js +10 -1
- package/dist/scripts/check-package-boundary.js +12 -3
- package/dist/scripts/codex-0-137-compat-check.js +27 -0
- package/dist/scripts/codex-environment-scoped-approvals-check.js +10 -0
- package/dist/scripts/codex-plugin-list-json-check.js +8 -0
- package/dist/scripts/codex-thread-runtime-choice-check.js +10 -0
- package/dist/scripts/local-collab-all-pipelines-final-gpt-check.js +21 -0
- package/dist/scripts/local-llm-all-pipelines-check.js +11 -0
- package/dist/scripts/local-llm-cache-performance-check.js +10 -0
- package/dist/scripts/local-llm-capability-check.js +14 -0
- package/dist/scripts/local-llm-smoke-check.js +23 -0
- package/dist/scripts/local-llm-structured-output-check.js +11 -0
- package/dist/scripts/local-llm-throughput-check.js +10 -0
- package/dist/scripts/local-llm-tool-call-repair-check.js +10 -0
- package/dist/scripts/local-llm-warmup-check.js +11 -0
- package/dist/scripts/naruto-active-pool-check.js +39 -0
- package/dist/scripts/naruto-concurrency-governor-check.js +52 -0
- package/dist/scripts/naruto-gpt-final-pack-check.js +34 -0
- package/dist/scripts/naruto-parallel-patch-apply-check.js +41 -0
- package/dist/scripts/naruto-readonly-routing-check.js +116 -0
- package/dist/scripts/naruto-real-local-gpt-final-smoke.js +16 -0
- package/dist/scripts/naruto-role-distribution-check.js +23 -0
- package/dist/scripts/naruto-shadow-clone-swarm-check.js +13 -0
- package/dist/scripts/naruto-verification-pool-check.js +36 -0
- package/dist/scripts/naruto-work-graph-check.js +24 -0
- package/dist/scripts/naruto-zellij-massive-ui-check.js +23 -0
- package/dist/scripts/product-design-auto-install-check.js +119 -0
- package/dist/scripts/product-design-plugin-routing-check.js +101 -0
- package/dist/scripts/prompt-placeholder-guard-check.js +33 -0
- package/dist/scripts/python-codex-sdk-all-pipelines-check.js +47 -0
- package/dist/scripts/python-codex-sdk-capability-check.js +75 -0
- package/dist/scripts/python-codex-sdk-sandbox-policy-check.js +10 -0
- package/dist/scripts/python-codex-sdk-stream-bridge-check.js +12 -0
- package/dist/scripts/release-parallel-check.js +16 -2
- package/dist/scripts/release-provenance-check.js +21 -0
- package/dist/scripts/release-real-check.js +5 -0
- package/dist/scripts/zellij-worker-pane-manager-check.js +1 -1
- package/package.json +36 -4
- package/schemas/local-llm/local-model-config.schema.json +74 -0
- package/schemas/naruto/naruto-concurrency-governor.schema.json +21 -0
- package/schemas/naruto/naruto-work-graph.schema.json +22 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export const NARUTO_WORK_KINDS = [
|
|
2
|
+
'implementation',
|
|
3
|
+
'code_modification',
|
|
4
|
+
'refactor',
|
|
5
|
+
'test_generation',
|
|
6
|
+
'test_execution',
|
|
7
|
+
'verification',
|
|
8
|
+
'research',
|
|
9
|
+
'documentation',
|
|
10
|
+
'ux_review',
|
|
11
|
+
'ppt_review',
|
|
12
|
+
'image_review',
|
|
13
|
+
'conflict_resolution',
|
|
14
|
+
'patch_rebase',
|
|
15
|
+
'rollback_preparation',
|
|
16
|
+
'integration_support',
|
|
17
|
+
'final_review_input_pack'
|
|
18
|
+
];
|
|
19
|
+
export const NARUTO_WRITE_WORK_KINDS = new Set([
|
|
20
|
+
'implementation',
|
|
21
|
+
'code_modification',
|
|
22
|
+
'refactor',
|
|
23
|
+
'test_generation',
|
|
24
|
+
'documentation',
|
|
25
|
+
'conflict_resolution',
|
|
26
|
+
'patch_rebase',
|
|
27
|
+
'rollback_preparation',
|
|
28
|
+
'integration_support'
|
|
29
|
+
]);
|
|
30
|
+
export function isNarutoWriteKind(kind) {
|
|
31
|
+
return NARUTO_WRITE_WORK_KINDS.has(kind);
|
|
32
|
+
}
|
|
33
|
+
export function normalizeNarutoWorkKind(value, fallback = 'verification') {
|
|
34
|
+
const text = String(value || '');
|
|
35
|
+
return NARUTO_WORK_KINDS.includes(text) ? text : fallback;
|
|
36
|
+
}
|
|
37
|
+
export function normalizeNarutoPath(value) {
|
|
38
|
+
return String(value || '').replace(/\\/g, '/').replace(/^\.\/+/, '').split('/').filter((part) => part && part !== '.').join('/');
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=naruto-work-item.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function stealNarutoWork(queue, input = {}) {
|
|
2
|
+
const item = queue.shift() || null;
|
|
3
|
+
return {
|
|
4
|
+
schema: 'sks.naruto-work-stealing.v1',
|
|
5
|
+
stolen: Boolean(item),
|
|
6
|
+
work_item_id: item?.id || null,
|
|
7
|
+
from_queue: input.fromQueue || 'pending',
|
|
8
|
+
to_slot: input.toSlot || 'idle-slot'
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=naruto-work-stealing.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export function monitorNarutoResourcePressure(probe, input = {}) {
|
|
2
|
+
const activeWorkers = Math.max(1, Math.floor(Number(input.activeWorkers || 1)));
|
|
3
|
+
const zellijCap = Math.max(1, Math.floor(Number(input.zellijVisiblePaneCap || 12)));
|
|
4
|
+
const memoryPressure = 1 - (probe.free_memory_bytes / Math.max(1, probe.total_memory_bytes));
|
|
5
|
+
const cpuPressure = Math.min(1, (probe.current_load_average[0] || 0) / Math.max(1, probe.cpu_core_count));
|
|
6
|
+
const fdPressure = Math.min(1, (activeWorkers * 6 + probe.process_count) / Math.max(1, probe.file_descriptor_limit));
|
|
7
|
+
const zellijPressure = Math.min(1, probe.zellij_pane_count / zellijCap);
|
|
8
|
+
const diskIoPressure = probe.disk_io_pressure;
|
|
9
|
+
const reasons = [
|
|
10
|
+
...(memoryPressure > 0.8 ? ['memory_cap'] : []),
|
|
11
|
+
...(cpuPressure > 0.9 ? ['cpu_load_cap'] : []),
|
|
12
|
+
...(fdPressure > 0.75 ? ['file_descriptor_budget'] : []),
|
|
13
|
+
...(zellijPressure > 0.9 ? ['zellij_ui_pane_budget'] : []),
|
|
14
|
+
...(diskIoPressure > 0.75 ? ['disk_io_pressure'] : [])
|
|
15
|
+
];
|
|
16
|
+
const maxPressure = Math.max(memoryPressure, cpuPressure, fdPressure, zellijPressure, diskIoPressure);
|
|
17
|
+
const state = maxPressure >= 0.92 ? 'saturated' : maxPressure >= 0.72 ? 'throttled' : 'normal';
|
|
18
|
+
return {
|
|
19
|
+
schema: 'sks.naruto-resource-pressure.v1',
|
|
20
|
+
state,
|
|
21
|
+
memory_pressure: round(memoryPressure),
|
|
22
|
+
cpu_pressure: round(cpuPressure),
|
|
23
|
+
fd_pressure: round(fdPressure),
|
|
24
|
+
zellij_pressure: round(zellijPressure),
|
|
25
|
+
disk_io_pressure: round(diskIoPressure),
|
|
26
|
+
reasons
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function round(value) {
|
|
30
|
+
return Math.round(Math.max(0, Math.min(1, value)) * 1000) / 1000;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=resource-pressure-monitor.js.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { nowIso, writeJsonAtomic } from '../fsx.js';
|
|
3
|
+
import { runGptFinalArbiter } from '../codex-control/gpt-final-arbiter.js';
|
|
4
|
+
import { gptFinalRequiredForPipeline } from './gpt-final-required.js';
|
|
5
|
+
export async function finalizePipelineResult(input) {
|
|
6
|
+
const cwd = path.resolve(input.cwd || process.cwd());
|
|
7
|
+
const root = path.resolve(input.mutationLedgerRoot || path.join(cwd, '.sneakoscope', 'tmp', 'pipeline-finalize', safeName(input.missionId)));
|
|
8
|
+
const requirement = gptFinalRequiredForPipeline({
|
|
9
|
+
localParticipated: input.localParticipated,
|
|
10
|
+
candidateResults: input.candidateResults
|
|
11
|
+
});
|
|
12
|
+
let arbiter = null;
|
|
13
|
+
let blockers = [];
|
|
14
|
+
if (requirement.gpt_final_required) {
|
|
15
|
+
arbiter = await runGptFinalArbiter({
|
|
16
|
+
schema: 'sks.gpt-final-arbiter-input.v1',
|
|
17
|
+
route: input.route,
|
|
18
|
+
mission_id: input.missionId,
|
|
19
|
+
local_mode: 'local-parallel-gpt-final',
|
|
20
|
+
local_outputs: input.candidateResults,
|
|
21
|
+
candidate_patch_envelopes: input.candidatePatchEnvelopes
|
|
22
|
+
}, {
|
|
23
|
+
cwd,
|
|
24
|
+
mutationLedgerRoot: path.join(root, 'gpt-final-arbiter'),
|
|
25
|
+
...(typeof input.forceGptFinalUnavailable === 'boolean' ? { forceUnavailable: input.forceGptFinalUnavailable } : {})
|
|
26
|
+
});
|
|
27
|
+
blockers = [
|
|
28
|
+
...(arbiter.ok ? [] : ['gpt_final_arbiter_required_not_passed']),
|
|
29
|
+
...(Array.isArray(arbiter.blockers) ? arbiter.blockers.map(String) : [])
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
const result = {
|
|
33
|
+
schema: 'sks.pipeline-finalize-result.v1',
|
|
34
|
+
generated_at: nowIso(),
|
|
35
|
+
ok: blockers.length === 0,
|
|
36
|
+
route: input.route,
|
|
37
|
+
mission_id: input.missionId,
|
|
38
|
+
local_participated: requirement.local_participated,
|
|
39
|
+
gpt_final_required: requirement.gpt_final_required,
|
|
40
|
+
gpt_final_arbiter: arbiter,
|
|
41
|
+
final_status: blockers.length ? 'blocked' : 'accepted',
|
|
42
|
+
apply_allowed: blockers.length === 0 && input.applyPatches === true,
|
|
43
|
+
final_patch_source: requirement.gpt_final_required ? 'gpt_final_arbiter' : 'deterministic_finalize',
|
|
44
|
+
candidate_results_count: input.candidateResults.length,
|
|
45
|
+
candidate_patch_envelope_count: input.candidatePatchEnvelopes.length,
|
|
46
|
+
verification_results_count: input.verificationResults.length,
|
|
47
|
+
side_effect_report: input.sideEffectReport,
|
|
48
|
+
mutation_ledger: input.mutationLedger,
|
|
49
|
+
rollback_plan: input.rollbackPlan,
|
|
50
|
+
blockers
|
|
51
|
+
};
|
|
52
|
+
await writeJsonAtomic(path.join(root, 'pipeline-finalize-result.json'), result);
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
function safeName(value) {
|
|
56
|
+
return String(value || 'unknown').replace(/[^a-zA-Z0-9_.-]+/g, '-').slice(0, 80);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=finalize-pipeline-result.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { localCollaborationParticipated } from '../local-llm/local-collaboration-policy.js';
|
|
2
|
+
export function gptFinalRequiredForPipeline(input) {
|
|
3
|
+
const localParticipated = input.localParticipated === true
|
|
4
|
+
|| localCollaborationParticipated(Array.isArray(input.candidateResults) ? input.candidateResults : []);
|
|
5
|
+
return {
|
|
6
|
+
schema: 'sks.gpt-final-required.v1',
|
|
7
|
+
local_participated: localParticipated,
|
|
8
|
+
gpt_final_required: localParticipated,
|
|
9
|
+
reason: localParticipated ? 'local_llm_outputs_are_drafts' : 'no_local_participation'
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=gpt-final-required.js.map
|
|
@@ -386,7 +386,7 @@ export function promptPipelineContext(prompt, route = null) {
|
|
|
386
386
|
skillDreamPolicyText(),
|
|
387
387
|
route?.id === 'PPT'
|
|
388
388
|
? `${pptPipelineAllowlistPolicyText()} ${getdesignReferencePolicyText()}`
|
|
389
|
-
: `Design routing: UI/UX
|
|
389
|
+
: `Design routing: UI/UX uses the Codex App Product Design plugin first for get-context/user-context, research/ideate, prototype/image-to-code/url-to-code, audit/design-qa, and share when available. Treat design.md, design-system-builder, design-ui-editor, design-artifact-expert, and getdesign-reference as compatibility fallback only when the Product Design plugin is unavailable or an existing local design.md must be preserved. Image/logo/raster assets use imagegen, which must prefer Codex App built-in image generation documented at ${CODEX_APP_IMAGE_GENERATION_DOC_URL}. ${CODEX_IMAGEGEN_REQUIRED_POLICY} ${getdesignReferencePolicyText()}`,
|
|
390
390
|
triwikiContextTrackingText(),
|
|
391
391
|
triwikiStagePolicyText(),
|
|
392
392
|
stackCurrentDocsPolicyText(),
|
package/dist/core/ppt.js
CHANGED
|
@@ -2,6 +2,7 @@ import path from 'node:path';
|
|
|
2
2
|
import fsp from 'node:fs/promises';
|
|
3
3
|
import { nowIso, readJson, sha256, writeJsonAtomic, writeTextAtomic } from './fsx.js';
|
|
4
4
|
import { AWESOME_DESIGN_MD_REFERENCE, CODEX_APP_IMAGE_GENERATION_DOC_URL, CODEX_IMAGEGEN_EVIDENCE_SOURCE, DESIGN_SYSTEM_SSOT, GETDESIGN_REFERENCE, PPT_CONDITIONAL_SKILL_ALLOWLIST, PPT_PIPELINE_MCP_ALLOWLIST, PPT_PIPELINE_SKILL_ALLOWLIST } from './routes.js';
|
|
5
|
+
import { PRODUCT_DESIGN_LEGACY_DESIGN_FALLBACK_SKILLS, PRODUCT_DESIGN_PIPELINE_STAGES, PRODUCT_DESIGN_PLUGIN, PRODUCT_DESIGN_REQUIRED_SKILLS } from './product-design-plugin.js';
|
|
5
6
|
export const PPT_AUDIENCE_STRATEGY_ARTIFACT = 'ppt-audience-strategy.json';
|
|
6
7
|
export const PPT_GATE_ARTIFACT = 'ppt-gate.json';
|
|
7
8
|
export const PPT_SOURCE_LEDGER_ARTIFACT = 'ppt-source-ledger.json';
|
|
@@ -868,28 +869,48 @@ export function buildPptStyleTokens(contract = {}) {
|
|
|
868
869
|
required_skills: [...PPT_PIPELINE_SKILL_ALLOWLIST],
|
|
869
870
|
conditional_skills: [...PPT_CONDITIONAL_SKILL_ALLOWLIST],
|
|
870
871
|
allowed_mcp_servers: [...PPT_PIPELINE_MCP_ALLOWLIST],
|
|
872
|
+
primary_design_plugin: PRODUCT_DESIGN_PLUGIN.id,
|
|
873
|
+
product_design_tools: [...PRODUCT_DESIGN_REQUIRED_SKILLS],
|
|
874
|
+
product_design_stage_map: [...PRODUCT_DESIGN_PIPELINE_STAGES],
|
|
871
875
|
ignore_installed_out_of_pipeline_skills: true,
|
|
872
|
-
ignored_design_skills_even_if_installed: [
|
|
873
|
-
anti_ai_design_goal: 'prevent AI-like generic presentation design by forcing decisions through audience, sources,
|
|
874
|
-
rule: 'PPT design and render work must use only the route allowlist. Installed skills or MCP servers outside this allowlist are ignored unless the sealed PPT contract explicitly activates a conditional entry.'
|
|
876
|
+
ignored_design_skills_even_if_installed: [...PRODUCT_DESIGN_LEGACY_DESIGN_FALLBACK_SKILLS],
|
|
877
|
+
anti_ai_design_goal: 'prevent AI-like generic presentation design by forcing decisions through Product Design plugin evidence, audience, sources, and route-local style tokens instead of freeform decorative design skills',
|
|
878
|
+
rule: 'PPT design and render work must use Product Design plugin first plus only the route allowlist. Installed skills or MCP servers outside this allowlist are ignored unless the sealed PPT contract explicitly activates a conditional entry.'
|
|
875
879
|
},
|
|
876
880
|
design_ssot: {
|
|
881
|
+
primary_authority: PRODUCT_DESIGN_PLUGIN.id,
|
|
877
882
|
authority: DESIGN_SYSTEM_SSOT.authority_file,
|
|
878
883
|
builder_prompt: DESIGN_SYSTEM_SSOT.builder_prompt,
|
|
879
884
|
route_local_artifact: PPT_STYLE_TOKENS_ARTIFACT,
|
|
880
|
-
|
|
885
|
+
mode: 'product_design_primary_with_local_fallback_cache',
|
|
886
|
+
rule: 'PPT style tokens are a route-local projection of Product Design plugin evidence when available; design.md/getdesign fallback inputs are selected, fused, and applied here rather than kept as independent authorities.'
|
|
887
|
+
},
|
|
888
|
+
product_design_plugin: {
|
|
889
|
+
id: PRODUCT_DESIGN_PLUGIN.id,
|
|
890
|
+
display_name: PRODUCT_DESIGN_PLUGIN.display_name,
|
|
891
|
+
marketplace: PRODUCT_DESIGN_PLUGIN.marketplace,
|
|
892
|
+
marketplace_kind: PRODUCT_DESIGN_PLUGIN.marketplace_kind,
|
|
893
|
+
remote_plugin_id: PRODUCT_DESIGN_PLUGIN.remote_plugin_id,
|
|
894
|
+
app_server_read_params: PRODUCT_DESIGN_PLUGIN.app_server.read_params,
|
|
895
|
+
required_skills: [...PRODUCT_DESIGN_REQUIRED_SKILLS],
|
|
896
|
+
stage_map: [...PRODUCT_DESIGN_PIPELINE_STAGES]
|
|
881
897
|
},
|
|
882
898
|
design_reference_selection: reference,
|
|
883
899
|
source_inputs: [
|
|
900
|
+
{
|
|
901
|
+
id: PRODUCT_DESIGN_PLUGIN.id,
|
|
902
|
+
url: PRODUCT_DESIGN_PLUGIN.marketplace,
|
|
903
|
+
role: 'primary_codex_app_design_plugin'
|
|
904
|
+
},
|
|
884
905
|
{
|
|
885
906
|
id: GETDESIGN_REFERENCE.id,
|
|
886
907
|
url: GETDESIGN_REFERENCE.url,
|
|
887
|
-
role: '
|
|
908
|
+
role: 'fallback_source_input_for_ssot'
|
|
888
909
|
},
|
|
889
910
|
{
|
|
890
911
|
id: AWESOME_DESIGN_MD_REFERENCE.id,
|
|
891
912
|
url: AWESOME_DESIGN_MD_REFERENCE.url,
|
|
892
|
-
role: '
|
|
913
|
+
role: 'fallback_source_input_for_ssot'
|
|
893
914
|
}
|
|
894
915
|
],
|
|
895
916
|
avoid: ['over-designed decoration', 'ornamental gradients', 'nested cards', 'low-contrast gray body text', 'excessive motion or effects'],
|
|
@@ -1130,13 +1151,15 @@ export function buildPptRenderReport({ contract = {}, audience, sourceLedger, fa
|
|
|
1130
1151
|
design_policy_checks: [
|
|
1131
1152
|
{ id: 'information_first', passed: styleTokens.design_policy?.priority === 'information_first' },
|
|
1132
1153
|
{ id: 'restrained_detail', passed: styleTokens.design_policy?.visual_style === 'simple_restrained_detailed' },
|
|
1133
|
-
{ id: '
|
|
1134
|
-
{ id: '
|
|
1154
|
+
{ id: 'product_design_plugin_declared', passed: styleTokens.design_policy?.product_design_plugin?.id === PRODUCT_DESIGN_PLUGIN.id && (styleTokens.design_policy?.product_design_plugin?.required_skills || []).includes('design-qa') },
|
|
1155
|
+
{ id: 'design_ssot_declared', passed: styleTokens.design_policy?.design_ssot?.authority === DESIGN_SYSTEM_SSOT.authority_file && styleTokens.design_policy?.design_ssot?.primary_authority === PRODUCT_DESIGN_PLUGIN.id },
|
|
1156
|
+
{ id: 'curated_design_md_input_fused', passed: (styleTokens.design_policy?.source_inputs || []).some((entry) => entry.url === AWESOME_DESIGN_MD_REFERENCE.url && /fallback_source_input/.test(entry.role || '')) },
|
|
1135
1157
|
{ id: 'concrete_design_reference_selected', passed: Boolean(styleTokens.design_policy?.design_reference_selection?.primary?.id && styleTokens.design_policy?.design_reference_selection?.selected_sources?.length) },
|
|
1136
1158
|
{ id: 'reference_rules_applied_to_tokens', passed: Boolean(styleTokens.layout?.composition && styleTokens.layout?.treatment && styleTokens.design_policy?.design_reference_selection?.applied_token_profile) },
|
|
1137
1159
|
{ id: 'html_uses_reference_layout', passed: typeof html === 'string' && html.includes('decision evidence') && html.includes(styleTokens.layout?.composition || 'presentation-grid') },
|
|
1138
1160
|
{ id: 'ppt_skill_allowlist_enforced', passed: JSON.stringify(styleTokens.design_policy?.pipeline_allowlist?.required_skills || []) === JSON.stringify([...PPT_PIPELINE_SKILL_ALLOWLIST]) },
|
|
1139
1161
|
{ id: 'out_of_pipeline_design_skills_ignored', passed: styleTokens.design_policy?.pipeline_allowlist?.ignore_installed_out_of_pipeline_skills === true && (styleTokens.design_policy?.pipeline_allowlist?.ignored_design_skills_even_if_installed || []).includes('design-artifact-expert') },
|
|
1162
|
+
{ id: 'legacy_design_skills_fallback_only', passed: styleTokens.design_policy?.pipeline_allowlist?.primary_design_plugin === PRODUCT_DESIGN_PLUGIN.id && (styleTokens.design_policy?.pipeline_allowlist?.ignored_design_skills_even_if_installed || []).includes('design-system-builder') },
|
|
1140
1163
|
{ id: 'ppt_mcp_allowlist_scoped', passed: (styleTokens.design_policy?.pipeline_allowlist?.allowed_mcp_servers || []).every((entry) => entry.mcp === 'context7' && /external_documentation/.test(entry.condition || '')) },
|
|
1141
1164
|
{ id: 'no_decorative_overdesign', passed: !String(html).includes('gradient') },
|
|
1142
1165
|
{ id: 'fact_ledger_embedded', passed: typeof html === 'string' && html.includes('ppt-fact-ledger') },
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { EMPTY_CODEX_INFO, getCodexInfo } from './codex-adapter.js';
|
|
3
|
+
import { PRODUCT_DESIGN_PLUGIN, normalizeProductDesignPluginEvidence } from './product-design-plugin.js';
|
|
4
|
+
export const PRODUCT_DESIGN_AUTO_INSTALL_ENV = 'SKS_PRODUCT_DESIGN_AUTO_INSTALL';
|
|
5
|
+
export function productDesignAutoInstallRequested(opts = {}) {
|
|
6
|
+
const env = opts.env || process.env;
|
|
7
|
+
return opts.autoInstallProductDesign === true
|
|
8
|
+
|| opts.installProductDesign === true
|
|
9
|
+
|| opts.requireProductDesign === true
|
|
10
|
+
|| opts.designRoute === true
|
|
11
|
+
|| env?.[PRODUCT_DESIGN_AUTO_INSTALL_ENV] === '1'
|
|
12
|
+
|| /^true$/i.test(String(env?.[PRODUCT_DESIGN_AUTO_INSTALL_ENV] || ''));
|
|
13
|
+
}
|
|
14
|
+
export async function ensureProductDesignPluginInstalled(opts = {}) {
|
|
15
|
+
const autoInstallProductDesign = productDesignAutoInstallRequested(opts);
|
|
16
|
+
const injectedRequest = opts.request || opts.appServerRequest;
|
|
17
|
+
if (injectedRequest) {
|
|
18
|
+
return ensureProductDesignPluginInstalledWithRequest(injectedRequest, {
|
|
19
|
+
...opts,
|
|
20
|
+
autoInstallProductDesign
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
const codex = opts.codex || await getCodexInfo().catch(() => EMPTY_CODEX_INFO);
|
|
24
|
+
if (!codex.bin) {
|
|
25
|
+
return productDesignAppServerUnavailable('codex_cli_missing', 'Codex CLI missing.', {
|
|
26
|
+
autoInstallProductDesign
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
const client = new CodexAppServerJsonRpcClient({
|
|
30
|
+
command: codex.bin,
|
|
31
|
+
args: opts.appServerArgs || ['app-server', '--stdio'],
|
|
32
|
+
env: opts.env || process.env,
|
|
33
|
+
timeoutMs: opts.timeoutMs || 20000,
|
|
34
|
+
cwd: opts.cwd || process.cwd()
|
|
35
|
+
});
|
|
36
|
+
try {
|
|
37
|
+
await client.initialize();
|
|
38
|
+
const result = await ensureProductDesignPluginInstalledWithRequest((method, params) => client.request(method, params), {
|
|
39
|
+
...opts,
|
|
40
|
+
autoInstallProductDesign
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
...result,
|
|
44
|
+
app_server_command: `${codex.bin} ${(opts.appServerArgs || ['app-server', '--stdio']).join(' ')}`
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
return productDesignAppServerUnavailable('product_design_app_server_request_failed', err?.message || String(err), {
|
|
49
|
+
autoInstallProductDesign,
|
|
50
|
+
stderr: client.stderr.trim()
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
await client.close();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export async function ensureProductDesignPluginInstalledWithRequest(request, opts = {}) {
|
|
58
|
+
const autoInstallProductDesign = productDesignAutoInstallRequested(opts);
|
|
59
|
+
const calls = [];
|
|
60
|
+
const errors = [];
|
|
61
|
+
const call = async (method, params) => {
|
|
62
|
+
calls.push({ method, params });
|
|
63
|
+
try {
|
|
64
|
+
return { ok: true, result: await request(method, params) };
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
const error = err?.message || String(err);
|
|
68
|
+
errors.push({ method, params, error });
|
|
69
|
+
return { ok: false, error };
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const before = await readProductDesignFromAppServer(call);
|
|
73
|
+
if (before.evidence.ok) {
|
|
74
|
+
return productDesignEnsureReport({
|
|
75
|
+
ok: true,
|
|
76
|
+
status: 'ready',
|
|
77
|
+
autoInstallProductDesign,
|
|
78
|
+
installAttempted: false,
|
|
79
|
+
before,
|
|
80
|
+
after: before,
|
|
81
|
+
calls,
|
|
82
|
+
errors,
|
|
83
|
+
blockers: []
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (!autoInstallProductDesign) {
|
|
87
|
+
return productDesignEnsureReport({
|
|
88
|
+
ok: false,
|
|
89
|
+
status: 'install_not_requested',
|
|
90
|
+
autoInstallProductDesign,
|
|
91
|
+
installAttempted: false,
|
|
92
|
+
before,
|
|
93
|
+
after: before,
|
|
94
|
+
calls,
|
|
95
|
+
errors,
|
|
96
|
+
blockers: uniqueStrings([
|
|
97
|
+
...before.evidence.blockers,
|
|
98
|
+
'product_design_auto_install_not_requested'
|
|
99
|
+
])
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
const install = await call('plugin/install', before.install_params || PRODUCT_DESIGN_PLUGIN.app_server.install_params);
|
|
103
|
+
const after = await readProductDesignFromAppServer(call, before.read_params || PRODUCT_DESIGN_PLUGIN.app_server.read_params);
|
|
104
|
+
let installedListEvidence = null;
|
|
105
|
+
if (!after.evidence.ok) {
|
|
106
|
+
const installedList = await call('plugin/installed', {});
|
|
107
|
+
const summary = installedList.ok ? findProductDesignPluginSummaryFromMarketplaces(installedList.result) : null;
|
|
108
|
+
if (summary) {
|
|
109
|
+
installedListEvidence = normalizeProductDesignPluginEvidence({
|
|
110
|
+
plugin: {
|
|
111
|
+
marketplaceName: summary.marketplaceName,
|
|
112
|
+
summary,
|
|
113
|
+
skills: []
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const ok = after.evidence.ok;
|
|
119
|
+
return productDesignEnsureReport({
|
|
120
|
+
ok,
|
|
121
|
+
status: ok ? 'installed' : 'install_unverified',
|
|
122
|
+
autoInstallProductDesign,
|
|
123
|
+
installAttempted: true,
|
|
124
|
+
installResponse: summarizeProductDesignInstallResponse(install.result),
|
|
125
|
+
installError: install.ok ? null : install.error,
|
|
126
|
+
before,
|
|
127
|
+
after,
|
|
128
|
+
installedListEvidence,
|
|
129
|
+
calls,
|
|
130
|
+
errors,
|
|
131
|
+
blockers: ok ? [] : uniqueStrings([
|
|
132
|
+
...after.evidence.blockers,
|
|
133
|
+
...(installedListEvidence?.blockers || []),
|
|
134
|
+
...(install.ok ? ['product_design_plugin_install_unverified'] : ['product_design_plugin_install_failed'])
|
|
135
|
+
])
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
export async function readProductDesignFromAppServer(call, preferredReadParams = PRODUCT_DESIGN_PLUGIN.app_server.read_params) {
|
|
139
|
+
const direct = await call('plugin/read', preferredReadParams);
|
|
140
|
+
if (direct.ok) {
|
|
141
|
+
return {
|
|
142
|
+
read_source: 'plugin/read',
|
|
143
|
+
read_params: preferredReadParams,
|
|
144
|
+
install_params: preferredReadParams,
|
|
145
|
+
evidence: normalizeProductDesignPluginEvidence(direct.result)
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const list = await call('plugin/list', PRODUCT_DESIGN_PLUGIN.app_server.list_params);
|
|
149
|
+
const summary = list.ok ? findProductDesignPluginSummaryFromMarketplaces(list.result) : null;
|
|
150
|
+
const discoveredReadParams = summary
|
|
151
|
+
? productDesignAppServerReadParamsFromSummary(summary)
|
|
152
|
+
: PRODUCT_DESIGN_PLUGIN.app_server.read_params;
|
|
153
|
+
if (summary) {
|
|
154
|
+
const discoveredRead = await call('plugin/read', discoveredReadParams);
|
|
155
|
+
if (discoveredRead.ok) {
|
|
156
|
+
return {
|
|
157
|
+
read_source: 'plugin/list+plugin/read',
|
|
158
|
+
read_params: discoveredReadParams,
|
|
159
|
+
install_params: discoveredReadParams,
|
|
160
|
+
evidence: normalizeProductDesignPluginEvidence(discoveredRead.result)
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const evidence = summary
|
|
165
|
+
? normalizeProductDesignPluginEvidence({
|
|
166
|
+
plugin: {
|
|
167
|
+
marketplaceName: summary.marketplaceName,
|
|
168
|
+
summary,
|
|
169
|
+
skills: []
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
: normalizeProductDesignPluginEvidence({});
|
|
173
|
+
return {
|
|
174
|
+
read_source: summary ? 'plugin/list_summary' : 'plugin/read_failed',
|
|
175
|
+
read_params: discoveredReadParams,
|
|
176
|
+
install_params: discoveredReadParams,
|
|
177
|
+
evidence: {
|
|
178
|
+
...evidence,
|
|
179
|
+
blockers: uniqueStrings([
|
|
180
|
+
...evidence.blockers,
|
|
181
|
+
'product_design_plugin_read_failed'
|
|
182
|
+
])
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
export function findProductDesignPluginSummaryFromMarketplaces(input = {}) {
|
|
187
|
+
const marketplaces = Array.isArray(input?.marketplaces) ? input.marketplaces : [];
|
|
188
|
+
for (const marketplace of marketplaces) {
|
|
189
|
+
const plugins = Array.isArray(marketplace?.plugins) ? marketplace.plugins : [];
|
|
190
|
+
for (const plugin of plugins) {
|
|
191
|
+
const remotePluginId = String(plugin?.remotePluginId || plugin?.remote_plugin_id || '');
|
|
192
|
+
const id = String(plugin?.id || '');
|
|
193
|
+
const name = String(plugin?.name || '');
|
|
194
|
+
const displayName = String(plugin?.displayName || plugin?.display_name || plugin?.interface?.displayName || '');
|
|
195
|
+
const matches = id === PRODUCT_DESIGN_PLUGIN.id
|
|
196
|
+
|| name === PRODUCT_DESIGN_PLUGIN.name
|
|
197
|
+
|| displayName === PRODUCT_DESIGN_PLUGIN.display_name
|
|
198
|
+
|| remotePluginId === PRODUCT_DESIGN_PLUGIN.remote_plugin_id;
|
|
199
|
+
if (matches) {
|
|
200
|
+
return {
|
|
201
|
+
...plugin,
|
|
202
|
+
displayName,
|
|
203
|
+
marketplaceName: plugin?.marketplaceName || marketplace?.name || PRODUCT_DESIGN_PLUGIN.marketplace,
|
|
204
|
+
remotePluginId
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
export function productDesignAppServerReadParamsFromSummary(summary = {}) {
|
|
212
|
+
return {
|
|
213
|
+
remoteMarketplaceName: summary.marketplaceName || PRODUCT_DESIGN_PLUGIN.marketplace,
|
|
214
|
+
pluginName: summary.remotePluginId || summary.remote_plugin_id || PRODUCT_DESIGN_PLUGIN.remote_plugin_id
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function productDesignEnsureReport(input) {
|
|
218
|
+
const remoteEvidence = input.after?.evidence || input.before?.evidence || normalizeProductDesignPluginEvidence({});
|
|
219
|
+
return {
|
|
220
|
+
schema: 'sks.product-design-app-server-ensure.v1',
|
|
221
|
+
ok: input.ok,
|
|
222
|
+
checked: true,
|
|
223
|
+
status: input.status,
|
|
224
|
+
auto_install_requested: input.autoInstallProductDesign === true,
|
|
225
|
+
install_attempted: input.installAttempted === true,
|
|
226
|
+
install_response: input.installResponse || null,
|
|
227
|
+
install_error: input.installError || null,
|
|
228
|
+
before_evidence: input.before?.evidence || null,
|
|
229
|
+
after_evidence: input.after?.evidence || null,
|
|
230
|
+
installed_list_evidence: input.installedListEvidence || null,
|
|
231
|
+
remote_evidence: remoteEvidence,
|
|
232
|
+
read_source: input.after?.read_source || input.before?.read_source || null,
|
|
233
|
+
read_params: input.after?.read_params || input.before?.read_params || PRODUCT_DESIGN_PLUGIN.app_server.read_params,
|
|
234
|
+
install_params: input.before?.install_params || PRODUCT_DESIGN_PLUGIN.app_server.install_params,
|
|
235
|
+
calls: input.calls || [],
|
|
236
|
+
errors: input.errors || [],
|
|
237
|
+
blockers: input.blockers || []
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function productDesignAppServerUnavailable(reason, error, extra = {}) {
|
|
241
|
+
const evidence = normalizeProductDesignPluginEvidence({});
|
|
242
|
+
return {
|
|
243
|
+
schema: 'sks.product-design-app-server-ensure.v1',
|
|
244
|
+
ok: false,
|
|
245
|
+
checked: false,
|
|
246
|
+
status: 'app_server_unavailable',
|
|
247
|
+
auto_install_requested: extra.autoInstallProductDesign === true,
|
|
248
|
+
install_attempted: false,
|
|
249
|
+
install_response: null,
|
|
250
|
+
install_error: error,
|
|
251
|
+
before_evidence: evidence,
|
|
252
|
+
after_evidence: evidence,
|
|
253
|
+
installed_list_evidence: null,
|
|
254
|
+
remote_evidence: evidence,
|
|
255
|
+
read_source: null,
|
|
256
|
+
read_params: PRODUCT_DESIGN_PLUGIN.app_server.read_params,
|
|
257
|
+
install_params: PRODUCT_DESIGN_PLUGIN.app_server.install_params,
|
|
258
|
+
calls: [],
|
|
259
|
+
errors: [{ reason, error }],
|
|
260
|
+
stderr: extra.stderr || '',
|
|
261
|
+
blockers: uniqueStrings([
|
|
262
|
+
...evidence.blockers,
|
|
263
|
+
reason
|
|
264
|
+
])
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function summarizeProductDesignInstallResponse(result = {}) {
|
|
268
|
+
if (!result)
|
|
269
|
+
return null;
|
|
270
|
+
return {
|
|
271
|
+
auth_policy: result.authPolicy || result.auth_policy || null,
|
|
272
|
+
apps_needing_auth: Array.isArray(result.appsNeedingAuth)
|
|
273
|
+
? result.appsNeedingAuth.map((app) => ({
|
|
274
|
+
id: app?.id || null,
|
|
275
|
+
name: app?.name || app?.displayName || app?.display_name || null
|
|
276
|
+
}))
|
|
277
|
+
: []
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
function uniqueStrings(values = []) {
|
|
281
|
+
return Array.from(new Set(values.filter(Boolean).map((value) => String(value))));
|
|
282
|
+
}
|
|
283
|
+
class CodexAppServerJsonRpcClient {
|
|
284
|
+
command;
|
|
285
|
+
args;
|
|
286
|
+
env;
|
|
287
|
+
cwd;
|
|
288
|
+
timeoutMs;
|
|
289
|
+
child;
|
|
290
|
+
nextId;
|
|
291
|
+
pending;
|
|
292
|
+
stdoutBuffer;
|
|
293
|
+
stderr;
|
|
294
|
+
constructor(config = {}) {
|
|
295
|
+
this.command = config.command;
|
|
296
|
+
this.args = config.args || ['app-server', '--stdio'];
|
|
297
|
+
this.env = config.env || process.env;
|
|
298
|
+
this.cwd = config.cwd || process.cwd();
|
|
299
|
+
this.timeoutMs = Number(config.timeoutMs || 20000);
|
|
300
|
+
this.child = null;
|
|
301
|
+
this.nextId = 1;
|
|
302
|
+
this.pending = new Map();
|
|
303
|
+
this.stdoutBuffer = '';
|
|
304
|
+
this.stderr = '';
|
|
305
|
+
}
|
|
306
|
+
async initialize() {
|
|
307
|
+
this.start();
|
|
308
|
+
const result = await this.request('initialize', {
|
|
309
|
+
clientInfo: {
|
|
310
|
+
name: 'sneakoscope-product-design',
|
|
311
|
+
title: 'Sneakoscope Product Design Installer',
|
|
312
|
+
version: '1.0.0'
|
|
313
|
+
},
|
|
314
|
+
capabilities: {
|
|
315
|
+
experimentalApi: true,
|
|
316
|
+
requestAttestation: false,
|
|
317
|
+
optOutNotificationMethods: []
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
this.notify('notifications/initialized', {});
|
|
321
|
+
return result;
|
|
322
|
+
}
|
|
323
|
+
start() {
|
|
324
|
+
if (this.child)
|
|
325
|
+
return;
|
|
326
|
+
this.child = spawn(this.command, this.args, {
|
|
327
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
328
|
+
shell: false,
|
|
329
|
+
env: this.env,
|
|
330
|
+
cwd: this.cwd
|
|
331
|
+
});
|
|
332
|
+
this.child.stdout?.on('data', (chunk) => this.handleStdout(chunk));
|
|
333
|
+
this.child.stderr?.on('data', (chunk) => {
|
|
334
|
+
this.stderr += chunk.toString('utf8');
|
|
335
|
+
if (this.stderr.length > 64 * 1024)
|
|
336
|
+
this.stderr = this.stderr.slice(-64 * 1024);
|
|
337
|
+
});
|
|
338
|
+
this.child.on('error', (err) => this.rejectAll(err));
|
|
339
|
+
this.child.on('close', (code) => {
|
|
340
|
+
this.rejectAll(new Error(`Codex app-server exited before response (code ${code ?? 'signal'}). ${this.stderr.trim()}`.trim()));
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
request(method, params) {
|
|
344
|
+
this.start();
|
|
345
|
+
const id = this.nextId++;
|
|
346
|
+
const message = { jsonrpc: '2.0', id, method, params };
|
|
347
|
+
return new Promise((resolve, reject) => {
|
|
348
|
+
const timer = setTimeout(() => {
|
|
349
|
+
this.pending.delete(id);
|
|
350
|
+
reject(new Error(`Codex app-server request timed out: ${method}. ${this.stderr.trim()}`.trim()));
|
|
351
|
+
}, this.timeoutMs);
|
|
352
|
+
timer.unref?.();
|
|
353
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
354
|
+
this.child?.stdin?.write(`${JSON.stringify(message)}\n`);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
notify(method, params) {
|
|
358
|
+
this.start();
|
|
359
|
+
this.child?.stdin?.write(`${JSON.stringify({ jsonrpc: '2.0', method, params })}\n`);
|
|
360
|
+
}
|
|
361
|
+
handleStdout(chunk) {
|
|
362
|
+
this.stdoutBuffer += chunk.toString('utf8');
|
|
363
|
+
const lines = this.stdoutBuffer.split(/\r?\n/);
|
|
364
|
+
this.stdoutBuffer = lines.pop() || '';
|
|
365
|
+
for (const line of lines) {
|
|
366
|
+
if (!line.trim())
|
|
367
|
+
continue;
|
|
368
|
+
let message;
|
|
369
|
+
try {
|
|
370
|
+
message = JSON.parse(line);
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (message.id === undefined || !this.pending.has(message.id))
|
|
376
|
+
continue;
|
|
377
|
+
const pending = this.pending.get(message.id);
|
|
378
|
+
if (!pending)
|
|
379
|
+
continue;
|
|
380
|
+
this.pending.delete(message.id);
|
|
381
|
+
clearTimeout(pending.timer);
|
|
382
|
+
if (message.error)
|
|
383
|
+
pending.reject(new Error(message.error.message || JSON.stringify(message.error)));
|
|
384
|
+
else
|
|
385
|
+
pending.resolve(message.result);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
rejectAll(err) {
|
|
389
|
+
for (const [id, pending] of this.pending.entries()) {
|
|
390
|
+
clearTimeout(pending.timer);
|
|
391
|
+
pending.reject(err);
|
|
392
|
+
this.pending.delete(id);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
async close() {
|
|
396
|
+
if (!this.child)
|
|
397
|
+
return;
|
|
398
|
+
const child = this.child;
|
|
399
|
+
this.child = null;
|
|
400
|
+
try {
|
|
401
|
+
child.stdin?.end();
|
|
402
|
+
}
|
|
403
|
+
catch { }
|
|
404
|
+
try {
|
|
405
|
+
child.kill('SIGTERM');
|
|
406
|
+
}
|
|
407
|
+
catch { }
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
//# sourceMappingURL=product-design-app-server.js.map
|