@awak-app/simy-cli 0.6.17 → 0.6.19
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 +17 -0
- package/package.json +1 -1
- package/src/agent.js +30 -9
- package/src/cli-contract.js +1 -1
- package/src/codex-plugins/simy/hooks/delivery-loop.mjs +4 -3
- package/src/codex-plugins/simy/hooks/minimal-sufficient.mjs +15 -0
- package/src/delivery-hooks/supervisor.js +2 -1
- package/src/desktop-access.js +2 -1
- package/src/desktop-host.js +197 -1
- package/src/native-development-report-completion.js +143 -0
- package/src/native-development-reports.js +137 -0
- package/src/orchestrator/checklist-continuation.js +5 -0
- package/src/orchestrator/delivery/WORKFLOW.md +12 -0
- package/src/orchestrator/delivery/references/state-model.md +187 -0
- package/src/orchestrator/independent-audit.js +2 -0
- package/src/orchestrator/instruction.js +2 -0
- package/src/orchestrator/loop.js +119 -34
- package/src/runner.js +31 -2
- package/src/sqm/claude-plugin.js +1 -0
- package/src/windows-bootstrap.js +2 -1
- package/src/windows-inbound.js +9 -0
package/src/orchestrator/loop.js
CHANGED
|
@@ -28,6 +28,10 @@ async function runCodingLoopOwned({
|
|
|
28
28
|
shouldStop = () => false,
|
|
29
29
|
consumeHumanGuidance = () => "",
|
|
30
30
|
}) {
|
|
31
|
+
if (resume && snapshot.attempts.at(-1)?.ci_review_refresh_pending) {
|
|
32
|
+
return recheckPrReadinessOwned({ snapshot, collectEvidence, checkSqm, executeIndependentAudit,
|
|
33
|
+
shouldStop, onUpdate, retryCiReview: true });
|
|
34
|
+
}
|
|
31
35
|
if (!resume && snapshot.attempts.length > 0 && !snapshot.metadata?.completed_worker_result) {
|
|
32
36
|
return recheckPrReadinessOwned({ snapshot, collectEvidence, checkSqm, onUpdate });
|
|
33
37
|
}
|
|
@@ -263,39 +267,7 @@ async function runCodingLoopOwned({
|
|
|
263
267
|
});
|
|
264
268
|
await publish(snapshot, "independent_auditing", onUpdate);
|
|
265
269
|
if (shouldStop()) return stopCodingLoop(snapshot, onUpdate);
|
|
266
|
-
|
|
267
|
-
const reviewedMatrix = attempt.observed_evidence?.minimum_completion?.matrix_sha256;
|
|
268
|
-
const reviewedEvidenceIdentity = independentAuditEvidenceIdentity(attempt);
|
|
269
|
-
const reviewedObjectiveDigest = snapshot.charter.thread_state?.objective_contract?.current_digest;
|
|
270
|
-
const reviewedObjectiveVersion = snapshot.charter.thread_state?.objective_contract?.version;
|
|
271
|
-
const auditExecution = await safeExecution(() =>
|
|
272
|
-
executeIndependentAudit({
|
|
273
|
-
attemptNumber,
|
|
274
|
-
backend: snapshot.charter.audit_backend,
|
|
275
|
-
instruction: auditInstruction,
|
|
276
|
-
charter: snapshot.charter,
|
|
277
|
-
attempt,
|
|
278
|
-
}),
|
|
279
|
-
);
|
|
280
|
-
if (shouldStop()) return stopCodingLoop(snapshot, onUpdate);
|
|
281
|
-
attempt.independent_audit = buildIndependentAudit(
|
|
282
|
-
auditExecution,
|
|
283
|
-
snapshot.charter,
|
|
284
|
-
attempt,
|
|
285
|
-
);
|
|
286
|
-
attempt.token_usage = mergeTokenUsage(attempt.token_usage, auditExecution.tokenUsage);
|
|
287
|
-
attempt.budget = refreshBudgetState(snapshot);
|
|
288
|
-
recordBudgetWarnings(snapshot, attempt);
|
|
289
|
-
if (attempt.budget.token_exhausted) {
|
|
290
|
-
await stopForBudget({ snapshot, attempt, reason: "token_budget_exhausted", onUpdate });
|
|
291
|
-
return snapshot;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
attempt.independent_audit.result_parse = normalizeResultParseDiagnostic(auditExecution.result_parse);
|
|
295
|
-
attempt.independent_audit.minimum_matrix_sha256 = reviewedMatrix;
|
|
296
|
-
attempt.independent_audit.evidence_identity_sha256 = reviewedEvidenceIdentity;
|
|
297
|
-
attempt.independent_audit.objective_digest = reviewedObjectiveDigest;
|
|
298
|
-
attempt.independent_audit.objective_version = reviewedObjectiveVersion;
|
|
270
|
+
if (await reviewAttempt({ snapshot, attempt, executeIndependentAudit, shouldStop, onUpdate })) return snapshot;
|
|
299
271
|
|
|
300
272
|
// Re-collect after the read-only auditor to catch any mutated HEAD or working tree.
|
|
301
273
|
attempt.observed_evidence = await safeEvidence(collectEvidence, attempt, { retries: snapshot.metadata?.completed_worker_result ? 2 : 0, shouldStop });
|
|
@@ -369,7 +341,63 @@ async function stopCodingLoop(snapshot, onUpdate) {
|
|
|
369
341
|
return snapshot;
|
|
370
342
|
}
|
|
371
343
|
|
|
372
|
-
async function
|
|
344
|
+
async function reviewAttempt({ snapshot, attempt, executeIndependentAudit, shouldStop, onUpdate }) {
|
|
345
|
+
const auditInstruction = buildIndependentAuditInstruction(snapshot.charter, attempt);
|
|
346
|
+
const reviewedMatrix = attempt.observed_evidence?.minimum_completion?.matrix_sha256;
|
|
347
|
+
const reviewedEvidenceIdentity = independentAuditEvidenceIdentity(attempt);
|
|
348
|
+
const reviewedChecks = structuredClone(attempt.observed_evidence?.github?.checks || []);
|
|
349
|
+
const reviewedObjectiveDigest = snapshot.charter.thread_state?.objective_contract?.current_digest;
|
|
350
|
+
const reviewedObjectiveVersion = snapshot.charter.thread_state?.objective_contract?.version;
|
|
351
|
+
const auditExecution = await safeExecution(() =>
|
|
352
|
+
executeIndependentAudit({
|
|
353
|
+
attemptNumber: attempt.attempt_number,
|
|
354
|
+
backend: snapshot.charter.audit_backend,
|
|
355
|
+
instruction: auditInstruction,
|
|
356
|
+
charter: snapshot.charter,
|
|
357
|
+
attempt,
|
|
358
|
+
}),
|
|
359
|
+
);
|
|
360
|
+
if (shouldStop()) { await stopCodingLoop(snapshot, onUpdate); return true; }
|
|
361
|
+
attempt.independent_audit = buildIndependentAudit(
|
|
362
|
+
auditExecution,
|
|
363
|
+
snapshot.charter,
|
|
364
|
+
attempt,
|
|
365
|
+
);
|
|
366
|
+
attempt.token_usage = mergeTokenUsage(attempt.token_usage, auditExecution.tokenUsage);
|
|
367
|
+
attempt.budget = refreshBudgetState(snapshot);
|
|
368
|
+
recordBudgetWarnings(snapshot, attempt);
|
|
369
|
+
if (attempt.budget.token_exhausted) {
|
|
370
|
+
await stopForBudget({ snapshot, attempt, reason: "token_budget_exhausted", onUpdate });
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
attempt.independent_audit.result_parse = normalizeResultParseDiagnostic(auditExecution.result_parse);
|
|
375
|
+
attempt.independent_audit.reviewed_checks = reviewedChecks;
|
|
376
|
+
attempt.independent_audit.minimum_matrix_sha256 = reviewedMatrix;
|
|
377
|
+
attempt.independent_audit.evidence_identity_sha256 = reviewedEvidenceIdentity;
|
|
378
|
+
attempt.independent_audit.objective_digest = reviewedObjectiveDigest;
|
|
379
|
+
attempt.independent_audit.objective_version = reviewedObjectiveVersion;
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Only a CI-only change to the already reviewed candidate may automatically
|
|
384
|
+
// renew its review. Head/base/preparation/acceptance changes need explicit work.
|
|
385
|
+
function ciReviewRefreshIdentity(attempt, previousEvidence) {
|
|
386
|
+
const reviewed = attempt.independent_audit;
|
|
387
|
+
const github = attempt.observed_evidence?.github;
|
|
388
|
+
if (!reviewed?.passed || !reviewed.evidence_identity_sha256 || !github?.available ||
|
|
389
|
+
!attempt.observed_evidence?.local?.available || !attempt.observed_evidence.local.working_tree_clean ||
|
|
390
|
+
attempt.observed_evidence.local.errors?.length || github.error || !github.required_checks_present || !github.checks_passing ||
|
|
391
|
+
!github.checks?.length || github.checks.some(check => check.state !== 'success' && check.state !== 'skipped')) return null;
|
|
392
|
+
const identity = independentAuditEvidenceIdentity(attempt);
|
|
393
|
+
if (identity === reviewed.evidence_identity_sha256) return null;
|
|
394
|
+
const prior = { ...attempt, observed_evidence: { ...attempt.observed_evidence,
|
|
395
|
+
github: { ...github, checks: reviewed.reviewed_checks || previousEvidence?.github?.checks } } };
|
|
396
|
+
return independentAuditEvidenceIdentity(prior) === reviewed.evidence_identity_sha256 ? identity : null;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
async function recheckPrReadinessOwned({ snapshot, collectEvidence, checkSqm = null, executeIndependentAudit, shouldStop = () => false, onUpdate, retryCiReview = false }) {
|
|
400
|
+
if (shouldStop()) return stopCodingLoop(snapshot, onUpdate);
|
|
373
401
|
if (!verifyObjective(snapshot.charter).valid) {
|
|
374
402
|
snapshot.implementation_gate_passed = false;
|
|
375
403
|
snapshot.done_gate_passed = false;
|
|
@@ -391,6 +419,7 @@ async function recheckPrReadinessOwned({ snapshot, collectEvidence, checkSqm = n
|
|
|
391
419
|
code: "OBJECTIVE_EVIDENCE_STALE", severity: "blocker", target: "objective_contract",
|
|
392
420
|
explanation: "Make a new attempt and independently review evidence against the current objective.", repairability: "manual",
|
|
393
421
|
}]);
|
|
422
|
+
if (retryCiReview) attempt.ci_review_refresh_pending = false;
|
|
394
423
|
await publish(snapshot, "waiting_human", onUpdate);
|
|
395
424
|
return snapshot;
|
|
396
425
|
}
|
|
@@ -398,16 +427,72 @@ async function recheckPrReadinessOwned({ snapshot, collectEvidence, checkSqm = n
|
|
|
398
427
|
attempt_number: attempt.attempt_number,
|
|
399
428
|
});
|
|
400
429
|
await publish(snapshot, "checking_pr", onUpdate);
|
|
430
|
+
const previousEvidence = attempt.observed_evidence;
|
|
431
|
+
// Recover an old review's CI basis only while the saved full identity still
|
|
432
|
+
// proves it. Later monitoring observations cannot reconstruct that history.
|
|
433
|
+
if (!attempt.independent_audit?.reviewed_checks &&
|
|
434
|
+
attempt.independent_audit?.evidence_identity_sha256 === independentAuditEvidenceIdentity(attempt) &&
|
|
435
|
+
Array.isArray(previousEvidence?.github?.checks)) {
|
|
436
|
+
attempt.independent_audit.reviewed_checks = structuredClone(previousEvidence.github.checks);
|
|
437
|
+
}
|
|
401
438
|
attempt.observed_evidence = await safeEvidence(collectEvidence, attempt);
|
|
439
|
+
if (shouldStop()) return stopCodingLoop(snapshot, onUpdate);
|
|
402
440
|
attempt.sqm = await safeSqm(checkSqm, attempt, attempt.sqm, "full");
|
|
441
|
+
if (shouldStop()) return stopCodingLoop(snapshot, onUpdate);
|
|
442
|
+
if (retryCiReview && attempt.observed_evidence?.local?.available && attempt.observed_evidence?.github?.available &&
|
|
443
|
+
independentAuditEvidenceIdentity(attempt) !== attempt.ci_review_refresh_identity) {
|
|
444
|
+
attempt.ci_review_refresh_pending = false;
|
|
445
|
+
snapshot.final_audit = humanGate("The candidate changed while its CI review was interrupted.", [{
|
|
446
|
+
code: "CI_REVIEW_CANDIDATE_CHANGED", severity: "blocker", target: "independent_audit",
|
|
447
|
+
explanation: "Reconcile the changed HEAD, base or evidence, then explicitly continue work; the prior review cannot certify this candidate.", repairability: "manual",
|
|
448
|
+
}]);
|
|
449
|
+
await publish(snapshot, "waiting_human", onUpdate);
|
|
450
|
+
return snapshot;
|
|
451
|
+
}
|
|
452
|
+
const retryIdentity = retryCiReview && attempt.ci_review_refresh_pending &&
|
|
453
|
+
independentAuditEvidenceIdentity(attempt) === attempt.ci_review_refresh_identity &&
|
|
454
|
+
attempt.observed_evidence?.local?.available && attempt.observed_evidence.local.working_tree_clean &&
|
|
455
|
+
!attempt.observed_evidence.local.errors?.length && attempt.observed_evidence?.github?.available &&
|
|
456
|
+
!attempt.observed_evidence.github.error && attempt.observed_evidence.github.checks_passing &&
|
|
457
|
+
attempt.observed_evidence.github.required_checks_present ? attempt.ci_review_refresh_identity : null;
|
|
458
|
+
const refreshIdentity = retryIdentity || ciReviewRefreshIdentity(attempt, previousEvidence);
|
|
459
|
+
if (executeIndependentAudit && refreshIdentity && (retryIdentity || attempt.ci_review_refresh_identity !== refreshIdentity)) {
|
|
460
|
+
const budget = canStartProvider(snapshot, { attemptNumber: attempt.attempt_number, phase: "reviewer" });
|
|
461
|
+
if (!budget.allowed) { await stopForBudget({ snapshot, attempt, reason: budget.reason, onUpdate }); return snapshot; }
|
|
462
|
+
// Persist before dispatch so an interrupted/failed review cannot repeat on
|
|
463
|
+
// every monitor tick. Explicit resume retries only this bound review.
|
|
464
|
+
attempt.ci_review_refresh_identity = refreshIdentity;
|
|
465
|
+
attempt.ci_review_refresh_pending = true;
|
|
466
|
+
appendEvent(snapshot, "independent_auditing", "CI completed; refreshing independent review of the same candidate.", {
|
|
467
|
+
attempt_number: attempt.attempt_number, evidence_identity: refreshIdentity,
|
|
468
|
+
});
|
|
469
|
+
await publish(snapshot, "independent_auditing", onUpdate);
|
|
470
|
+
if (shouldStop()) return stopCodingLoop(snapshot, onUpdate);
|
|
471
|
+
if (await reviewAttempt({ snapshot, attempt, executeIndependentAudit, shouldStop, onUpdate })) return snapshot;
|
|
472
|
+
if (!attempt.independent_audit.passed &&
|
|
473
|
+
!attempt.independent_audit.findings.some(finding => finding.code === "INDEPENDENT_AUDIT_UNAVAILABLE")) {
|
|
474
|
+
// A completed adverse review belongs to the normal repair path. Only
|
|
475
|
+
// unavailable/interrupted reviews need a reviewer-only retry.
|
|
476
|
+
attempt.ci_review_refresh_pending = false;
|
|
477
|
+
}
|
|
478
|
+
attempt.observed_evidence = await safeEvidence(collectEvidence, attempt);
|
|
479
|
+
if (shouldStop()) return stopCodingLoop(snapshot, onUpdate);
|
|
480
|
+
attempt.sqm = await safeSqm(checkSqm, attempt, attempt.sqm, "full");
|
|
481
|
+
if (shouldStop()) return stopCodingLoop(snapshot, onUpdate);
|
|
482
|
+
}
|
|
403
483
|
attempt.audit = await auditAttempt(snapshot.charter, attempt, {
|
|
404
484
|
previousAttempt: snapshot.attempts.at(-2) ?? null,
|
|
405
485
|
sqm: attempt.sqm,
|
|
406
486
|
});
|
|
487
|
+
if (shouldStop()) return stopCodingLoop(snapshot, onUpdate);
|
|
407
488
|
attempt.outcome_progress = assessOutcomeProgress(
|
|
408
489
|
snapshot.charter, attempt, snapshot.attempts.at(-2) ?? null,
|
|
409
490
|
outcomeOptions(snapshot),
|
|
410
491
|
);
|
|
492
|
+
if (attempt.ci_review_refresh_pending && attempt.audit.passed && attempt.independent_audit?.passed &&
|
|
493
|
+
attempt.independent_audit.evidence_identity_sha256 === independentAuditEvidenceIdentity(attempt)) {
|
|
494
|
+
attempt.ci_review_refresh_pending = false;
|
|
495
|
+
}
|
|
411
496
|
snapshot.implementation_gate_passed = attempt.audit.passed;
|
|
412
497
|
attempt.pr_readiness = evaluatePrReadiness(snapshot.charter, attempt);
|
|
413
498
|
attempt.pr_gate = attempt.pr_readiness;
|
package/src/runner.js
CHANGED
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
normalizeWorkflowStartIntent,
|
|
52
52
|
} from "./workflow-observation-sender.js";
|
|
53
53
|
import { saveCodingRunSnapshot } from "./coding-run-store.js";
|
|
54
|
-
import { acquireDesktopProcessLock } from "./desktop-process-lock.js";
|
|
54
|
+
import { acquireDesktopProcessLock, PROCESS_CONFLICT_CODE } from "./desktop-process-lock.js";
|
|
55
55
|
import {
|
|
56
56
|
assertRecoveryActor, completedWorkerResult, recoveryActorBinding, refreshRecoveryAudit, refreshRecoveryBudgetAudit,
|
|
57
57
|
} from "./coding-run-recovery.js";
|
|
@@ -924,7 +924,24 @@ async function cleanupLocalAttachments(run) {
|
|
|
924
924
|
}
|
|
925
925
|
}
|
|
926
926
|
|
|
927
|
-
export async function recheckLocalCodingRun(run,
|
|
927
|
+
export async function recheckLocalCodingRun(run, options = {}) {
|
|
928
|
+
if (!run?.snapshot?.attempts?.length) return run?.snapshot || null;
|
|
929
|
+
if (!["pr_ready_for_review", "merge_ready"].includes(run.status)) return run.snapshot;
|
|
930
|
+
if (run.operation || run.child || run.stopRequested) return run.snapshot;
|
|
931
|
+
const operation = (async () => {
|
|
932
|
+
assertRecoveryActor(run);
|
|
933
|
+
const lease = run.localStateRoot ? await acquireDesktopProcessLock(
|
|
934
|
+
path.join(run.localStateRoot, ".controllers", executionTextSha256(run.id)),
|
|
935
|
+
) : null;
|
|
936
|
+
try { return await recheckLocalCodingRunOwned(run, options); }
|
|
937
|
+
finally { await lease?.release(); }
|
|
938
|
+
})();
|
|
939
|
+
run.operation = operation;
|
|
940
|
+
try { return await operation; }
|
|
941
|
+
finally { if (run.operation === operation) run.operation = null; }
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
async function recheckLocalCodingRunOwned(run, { collectEvidence, executeIndependentAudit, authorizeReview } = {}) {
|
|
928
945
|
if (!run?.snapshot?.attempts?.length) return run?.snapshot || null;
|
|
929
946
|
if (!["pr_ready_for_review", "merge_ready"].includes(run.status)) return run.snapshot;
|
|
930
947
|
const repositoryPath = await resolveRepositoryPath(run.request);
|
|
@@ -956,6 +973,15 @@ export async function recheckLocalCodingRun(run, { collectEvidence } = {}) {
|
|
|
956
973
|
await recheckPrReadiness({
|
|
957
974
|
snapshot: run.snapshot,
|
|
958
975
|
collectEvidence: evidenceCollector,
|
|
976
|
+
executeIndependentAudit: async context => {
|
|
977
|
+
await authorizeReview?.();
|
|
978
|
+
assertRecoveryActor(run);
|
|
979
|
+
if (run.stopRequested) throw new Error("Review cancelled by the local operator.");
|
|
980
|
+
return executeIndependentAudit ? executeIndependentAudit(context) : executeProcessAttempt({
|
|
981
|
+
...context, repositoryPath, run, marker: "SIMY_AUDIT_JSON:",
|
|
982
|
+
});
|
|
983
|
+
},
|
|
984
|
+
shouldStop: () => run.stopRequested,
|
|
959
985
|
checkSqm: sqmChecker,
|
|
960
986
|
onUpdate: async (snapshot) => {
|
|
961
987
|
run.snapshot = snapshot;
|
|
@@ -966,6 +992,8 @@ export async function recheckLocalCodingRun(run, { collectEvidence } = {}) {
|
|
|
966
992
|
}
|
|
967
993
|
|
|
968
994
|
export async function markCodingRunMonitorFailure(run, error) {
|
|
995
|
+
// A losing controller never owns the right to persist its stale snapshot.
|
|
996
|
+
if (run.stopRequested || run.status === "stopped" || error?.code === PROCESS_CONFLICT_CODE) return run.snapshot;
|
|
969
997
|
const message = error instanceof Error ? error.message : String(error);
|
|
970
998
|
run.snapshot.done_gate_passed = false;
|
|
971
999
|
run.snapshot.final_audit = {
|
|
@@ -1026,6 +1054,7 @@ export async function executeProcessAttempt({
|
|
|
1026
1054
|
model: phase === "reviewer" ? charter?.audit_model : charter?.model,
|
|
1027
1055
|
reasoningEffort: phase === "reviewer" ? charter?.audit_reasoning_effort : charter?.reasoning_effort,
|
|
1028
1056
|
});
|
|
1057
|
+
if (run.stopRequested) throw new Error("Execution cancelled by the local operator.");
|
|
1029
1058
|
const startedAt = new Date().toISOString();
|
|
1030
1059
|
const stdout = [];
|
|
1031
1060
|
const stderr = [];
|
package/src/sqm/claude-plugin.js
CHANGED
|
@@ -22,6 +22,7 @@ export async function claudePluginFiles() {
|
|
|
22
22
|
files.set(relative, content.replaceAll("current Codex thread", "current Claude Code conversation"));
|
|
23
23
|
}
|
|
24
24
|
files.set("hooks/delivery-loop.mjs", await readFile(path.join(SOURCE, "hooks/delivery-loop.mjs"), "utf8"));
|
|
25
|
+
files.set("hooks/minimal-sufficient.mjs", await readFile(path.join(SOURCE, "hooks/minimal-sufficient.mjs"), "utf8"));
|
|
25
26
|
files.set("hooks/outcome-progress.mjs", await readFile(path.join(SOURCE, "hooks/outcome-progress.mjs"), "utf8"));
|
|
26
27
|
files.set("hooks/continuation-policy.mjs", await readFile(path.join(SOURCE, "hooks/continuation-policy.mjs"), "utf8"));
|
|
27
28
|
files.set("hooks/action-association.mjs", await readFile(path.join(SOURCE, "hooks/action-association.mjs"), "utf8"));
|
package/src/windows-bootstrap.js
CHANGED
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { existsSync } from 'node:fs';
|
|
4
4
|
import { spawn } from 'node:child_process';
|
|
5
5
|
import { maintainWindowsUpdate, selectedWindowsRoot } from './windows-update.js';
|
|
6
|
+
import { desktopArguments } from './windows-inbound.js';
|
|
6
7
|
const installation = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
7
8
|
const stateRoot = path.join(process.env.LOCALAPPDATA, 'SIMY CLI', 'updates');
|
|
8
9
|
const argv = process.argv.slice(2);
|
|
@@ -22,7 +23,7 @@ if (updating) {
|
|
|
22
23
|
const desktop = argv[0] === '--desktop';
|
|
23
24
|
if (desktop && !existsSync(path.join(root, 'simy-desktop.exe'))) root = installation;
|
|
24
25
|
const child = spawn(path.join(root, desktop ? 'simy-desktop.exe' : 'runtime/node.exe'),
|
|
25
|
-
desktop ?
|
|
26
|
+
desktop ? desktopArguments(argv) : [path.join(root, 'cli/src/index.js'), ...argv], {
|
|
26
27
|
stdio: 'inherit', env: { ...process.env, ...(desktop ? {} : { SIMY_AUTO_UPDATE: '0' }) },
|
|
27
28
|
});
|
|
28
29
|
// Windows console signals are already delivered to both processes.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Keep the URI handoff separate from update and launch code so its exact
|
|
2
|
+
// command shape is covered by a platform-neutral test. desktop.cs remains the
|
|
3
|
+
// authority that validates the URI before it changes a visible selection.
|
|
4
|
+
export function desktopArguments(argv) {
|
|
5
|
+
// This internal marker distinguishes the desktop spawned by the update
|
|
6
|
+
// bootstrap from the bootstrap itself. It carries no inbound URI.
|
|
7
|
+
if (argv.length === 1) return ['--selected'];
|
|
8
|
+
return ['--selected', ...argv.slice(1)];
|
|
9
|
+
}
|