codex-workflow-v2 2.0.0-beta.12.18 → 2.0.0-beta.12.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 +6 -2
- package/dist/src/cli.js +1 -0
- package/dist/src/cli.js.map +1 -1
- package/dist/src/contracts.d.ts +1 -1
- package/dist/src/dependency-provenance.js +11 -0
- package/dist/src/dependency-provenance.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/workflow.d.ts +2 -0
- package/dist/src/workflow.js +223 -20
- package/dist/src/workflow.js.map +1 -1
- package/docs/pdf/codex-workflow-v2-architecture-ru.pdf +0 -0
- package/docs/pdf/codex-workflow-v2-chat-only-guide-ru.pdf +0 -0
- package/docs/pdf/codex-workflow-v2-technical-reference-ru.pdf +0 -0
- package/docs/pdf/sources/codex-workflow-v2-architecture-ru.md +14 -5
- package/docs/pdf/sources/codex-workflow-v2-chat-only-guide-ru.md +10 -9
- package/docs/pdf/sources/codex-workflow-v2-technical-reference-ru.md +26 -8
- package/docs/release.md +1 -1
- package/docs/updating-existing-project.md +23 -0
- package/docs/validation-report.md +78 -80
- package/package.json +1 -1
package/dist/src/workflow.js
CHANGED
|
@@ -2401,15 +2401,22 @@ export class WorkflowService {
|
|
|
2401
2401
|
const downstreamProofCarryover = step.status === 'planned' && dirty.length > 0
|
|
2402
2402
|
? assessDownstreamProofCarryover(this.store, context.identity.repositoryRoot, task, stepId)
|
|
2403
2403
|
: null;
|
|
2404
|
+
const planIntegrityDirtyCarryover = step.status === 'planned' && dirty.length > 0
|
|
2405
|
+
? this.assessPostRebindCheckSupportDirtyCarryover(context.identity.repositoryRoot, task, stepId)
|
|
2406
|
+
: null;
|
|
2404
2407
|
if (step.status === 'planned'
|
|
2405
2408
|
&& dirty.length > 0
|
|
2406
2409
|
&& !resumesCorrectiveReplan
|
|
2407
|
-
&& !downstreamProofCarryover?.eligible
|
|
2410
|
+
&& !downstreamProofCarryover?.eligible
|
|
2411
|
+
&& !planIntegrityDirtyCarryover?.eligible) {
|
|
2408
2412
|
throw new WorkflowError('GIT_PRECONDITION_FAILED', `${stepId} must start from a clean checkout.`, {
|
|
2409
2413
|
changedFiles: dirty,
|
|
2410
2414
|
...(downstreamProofCarryover?.applicable
|
|
2411
2415
|
? { downstreamProofCarryoverBlockers: downstreamProofCarryover.blockers }
|
|
2412
2416
|
: {}),
|
|
2417
|
+
...(planIntegrityDirtyCarryover?.applicable
|
|
2418
|
+
? { planIntegrityDirtyCarryoverBlockers: planIntegrityDirtyCarryover.blockers }
|
|
2419
|
+
: {}),
|
|
2413
2420
|
});
|
|
2414
2421
|
}
|
|
2415
2422
|
const dirtyOutsideStep = dirty.filter((file) => !pathAllowed(file, allowedWrites));
|
|
@@ -3643,6 +3650,17 @@ export class WorkflowService {
|
|
|
3643
3650
|
const adoptedCompatibility = assessment
|
|
3644
3651
|
? null
|
|
3645
3652
|
: this.postRebindCheckSupportCompatibility(snapshot, task);
|
|
3653
|
+
const dirtyCarryoverAssessment = assessment || adoptedCompatibility
|
|
3654
|
+
? null
|
|
3655
|
+
: this.assessPostRebindCheckSupportDirtyCarryover(snapshot.identity.repositoryRoot, task);
|
|
3656
|
+
const dirtyCarryoverCompatibility = dirtyCarryoverAssessment?.eligible
|
|
3657
|
+
&& dirtyCarryoverAssessment.compatibility
|
|
3658
|
+
? {
|
|
3659
|
+
...dirtyCarryoverAssessment.compatibility,
|
|
3660
|
+
planHash: task.planHash,
|
|
3661
|
+
mode: 'post-rebind-dirty-carryover',
|
|
3662
|
+
}
|
|
3663
|
+
: null;
|
|
3646
3664
|
const versions = inspectWorkflowVersions(snapshot.identity.repositoryRoot, [task.baseBranch]);
|
|
3647
3665
|
const versionSurfaces = {
|
|
3648
3666
|
declared: versions.declared,
|
|
@@ -3658,9 +3676,11 @@ export class WorkflowService {
|
|
|
3658
3676
|
const targetPatchOrdinal = targetPatch === null ? null : Number(targetPatch);
|
|
3659
3677
|
const blockers = [
|
|
3660
3678
|
...(assessments.length > 1 ? ['Exactly one failed Step may expose a Plan-integrity conflict.'] : []),
|
|
3661
|
-
...(assessment?.blockers ?? (adoptedCompatibility
|
|
3679
|
+
...(assessment?.blockers ?? (adoptedCompatibility || dirtyCarryoverCompatibility
|
|
3662
3680
|
? []
|
|
3663
|
-
:
|
|
3681
|
+
: (dirtyCarryoverAssessment?.blockers.length
|
|
3682
|
+
? dirtyCarryoverAssessment.blockers
|
|
3683
|
+
: ['No eligible Plan-integrity conflict, post-rebind scope adoption, or dirty carryover was found.']))),
|
|
3664
3684
|
...(sourcePatchOrdinal === null
|
|
3665
3685
|
|| targetPatchOrdinal === null
|
|
3666
3686
|
|| sourcePatchOrdinal < 11
|
|
@@ -3675,21 +3695,29 @@ export class WorkflowService {
|
|
|
3675
3695
|
];
|
|
3676
3696
|
return {
|
|
3677
3697
|
readOnly: true,
|
|
3678
|
-
eligible: Boolean(assessment?.eligible || adoptedCompatibility) && blockers.length === 0,
|
|
3679
|
-
action: (assessment?.eligible || adoptedCompatibility) && blockers.length === 0
|
|
3698
|
+
eligible: Boolean(assessment?.eligible || adoptedCompatibility || dirtyCarryoverCompatibility) && blockers.length === 0,
|
|
3699
|
+
action: (assessment?.eligible || adoptedCompatibility || dirtyCarryoverCompatibility) && blockers.length === 0
|
|
3680
3700
|
? 'update plan-integrity-transport'
|
|
3681
3701
|
: null,
|
|
3682
3702
|
projectId: task.projectId,
|
|
3683
3703
|
taskId: task.id,
|
|
3684
3704
|
taskRevision: task.revision,
|
|
3685
|
-
stepId: assessment?.stepId ?? adoptedCompatibility?.stepId ?? null,
|
|
3705
|
+
stepId: assessment?.stepId ?? adoptedCompatibility?.stepId ?? dirtyCarryoverCompatibility?.stepId ?? null,
|
|
3686
3706
|
sourceVersion,
|
|
3687
3707
|
targetVersion: PACKAGE_VERSION,
|
|
3688
3708
|
taskBranch: task.taskBranch,
|
|
3689
3709
|
baseBranch: task.baseBranch,
|
|
3690
|
-
dirtyFiles: assessment?.evidence?.changedFiles
|
|
3691
|
-
|
|
3692
|
-
|
|
3710
|
+
dirtyFiles: assessment?.evidence?.changedFiles
|
|
3711
|
+
?? adoptedCompatibility?.dirtyFiles
|
|
3712
|
+
?? dirtyCarryoverCompatibility?.dirtyFiles
|
|
3713
|
+
?? [],
|
|
3714
|
+
conflictHash: assessment?.evidence?.conflictHash
|
|
3715
|
+
?? adoptedCompatibility?.conflictHash
|
|
3716
|
+
?? dirtyCarryoverCompatibility?.conflictHash
|
|
3717
|
+
?? null,
|
|
3718
|
+
compatibilityMode: adoptedCompatibility?.mode
|
|
3719
|
+
?? dirtyCarryoverCompatibility?.mode
|
|
3720
|
+
?? 'failed-step-conflict',
|
|
3693
3721
|
requiredTransport: [
|
|
3694
3722
|
'Commit only package.json and package-lock.json on the active Milestone base.',
|
|
3695
3723
|
'Fast-forward or apply only that dependency commit to the Task branch while preserving all product bytes.',
|
|
@@ -3728,10 +3756,16 @@ export class WorkflowService {
|
|
|
3728
3756
|
const adoptedCompatibility = assessment?.evidence
|
|
3729
3757
|
? null
|
|
3730
3758
|
: this.postRebindCheckSupportCompatibility(snapshot, task);
|
|
3759
|
+
const dirtyCarryoverCompatibility = assessment?.evidence || adoptedCompatibility
|
|
3760
|
+
? null
|
|
3761
|
+
: this.carriedPostRebindCheckSupportDirtyCarryover(snapshot, task);
|
|
3731
3762
|
const carriedCompatibility = !assessment?.evidence && task.planHash
|
|
3732
3763
|
? this.carriedPlanIntegrityUpdateCompatibility(snapshot, task)
|
|
3733
3764
|
: null;
|
|
3734
|
-
if ((!assessment?.evidence
|
|
3765
|
+
if ((!assessment?.evidence
|
|
3766
|
+
&& !carriedCompatibility
|
|
3767
|
+
&& !adoptedCompatibility
|
|
3768
|
+
&& !dirtyCarryoverCompatibility) || !task.planHash) {
|
|
3735
3769
|
const ordinary = this.inspectDependencyProvenanceCandidate(snapshot, task);
|
|
3736
3770
|
return {
|
|
3737
3771
|
...ordinary,
|
|
@@ -3745,16 +3779,19 @@ export class WorkflowService {
|
|
|
3745
3779
|
])],
|
|
3746
3780
|
};
|
|
3747
3781
|
}
|
|
3748
|
-
const compatibility = carriedCompatibility
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
: assessment.evidence.
|
|
3757
|
-
|
|
3782
|
+
const compatibility = carriedCompatibility
|
|
3783
|
+
?? adoptedCompatibility
|
|
3784
|
+
?? dirtyCarryoverCompatibility
|
|
3785
|
+
?? {
|
|
3786
|
+
stepId: assessment.stepId,
|
|
3787
|
+
planHash: task.planHash,
|
|
3788
|
+
conflictHash: assessment.evidence.conflictHash,
|
|
3789
|
+
dirtyFiles: [...assessment.evidence.changedFiles],
|
|
3790
|
+
dirtyWorktreeHash: hashDirtyWorktree(snapshot.identity.repositoryRoot, assessment.evidence.changedFiles),
|
|
3791
|
+
remediationEventIds: assessment.evidence.kind === 'missing-npm-script-outside-allowed-writes'
|
|
3792
|
+
? [assessment.evidence.remediationEventId]
|
|
3793
|
+
: assessment.evidence.remediationEvents.map((event) => event.eventId),
|
|
3794
|
+
};
|
|
3758
3795
|
return this.inspectDependencyProvenanceCandidate(snapshot, task, null, null, null, compatibility);
|
|
3759
3796
|
}
|
|
3760
3797
|
downstreamProofReplanDependencyRecoveryPreflight(repository, taskId) {
|
|
@@ -5185,6 +5222,11 @@ export class WorkflowService {
|
|
|
5185
5222
|
&& changedFiles(repositoryRoot).length > 0
|
|
5186
5223
|
? assessDownstreamProofCarryover(this.store, repositoryRoot, task, nextStepId)
|
|
5187
5224
|
: null;
|
|
5225
|
+
const planIntegrityDirtyCarryover = next.action === 'task run'
|
|
5226
|
+
&& nextStepId
|
|
5227
|
+
&& changedFiles(repositoryRoot).length > 0
|
|
5228
|
+
? this.assessPostRebindCheckSupportDirtyCarryover(repositoryRoot, task, nextStepId)
|
|
5229
|
+
: null;
|
|
5188
5230
|
let mechanicalFeasibilityOverlay = {};
|
|
5189
5231
|
if (task.status === 'awaiting_execution_authorization' && task.planHash) {
|
|
5190
5232
|
try {
|
|
@@ -5736,6 +5778,29 @@ export class WorkflowService {
|
|
|
5736
5778
|
},
|
|
5737
5779
|
}
|
|
5738
5780
|
: {}),
|
|
5781
|
+
...(planIntegrityDirtyCarryover?.applicable
|
|
5782
|
+
? planIntegrityDirtyCarryover.eligible
|
|
5783
|
+
? {
|
|
5784
|
+
planIntegrityDirtyCarryover: {
|
|
5785
|
+
state: 'validated',
|
|
5786
|
+
stepId: nextStepId,
|
|
5787
|
+
dependencyRecoveryCommit: planIntegrityDirtyCarryover.dependencyRecoveryCommit,
|
|
5788
|
+
dirtyFiles: planIntegrityDirtyCarryover.compatibility?.dirtyFiles ?? [],
|
|
5789
|
+
dirtyWorktreeHash: planIntegrityDirtyCarryover.compatibility?.dirtyWorktreeHash ?? null,
|
|
5790
|
+
scopeExtensions: planIntegrityDirtyCarryover.compatibility?.scopeExtensions ?? [],
|
|
5791
|
+
meaning: 'The current Step may adopt exactly these hash-bound check-support bytes on task run.',
|
|
5792
|
+
},
|
|
5793
|
+
}
|
|
5794
|
+
: {
|
|
5795
|
+
action: 'doctor',
|
|
5796
|
+
blockedAction: 'task run',
|
|
5797
|
+
planIntegrityDirtyCarryover: {
|
|
5798
|
+
state: 'blocked',
|
|
5799
|
+
stepId: nextStepId,
|
|
5800
|
+
blockers: planIntegrityDirtyCarryover.blockers,
|
|
5801
|
+
},
|
|
5802
|
+
}
|
|
5803
|
+
: {}),
|
|
5739
5804
|
...(delegatedApprovalOptions.length > 0 ? { delegatedApprovalOptions } : {}),
|
|
5740
5805
|
...(correctiveDerivedAuthority && correctiveDerivedActor
|
|
5741
5806
|
? {
|
|
@@ -6133,6 +6198,144 @@ export class WorkflowService {
|
|
|
6133
6198
|
});
|
|
6134
6199
|
return candidates.length === 1 ? candidates[0] : null;
|
|
6135
6200
|
}
|
|
6201
|
+
carriedPostRebindCheckSupportDirtyCarryover(snapshot, task) {
|
|
6202
|
+
let boundaryCommit = headCommit(snapshot.identity.repositoryRoot);
|
|
6203
|
+
let dependencyCommitCount = 0;
|
|
6204
|
+
while (!task.systemCommits.includes(boundaryCommit)) {
|
|
6205
|
+
const dependencyCommit = inspectBoundedWorkflowDependencyCommit(snapshot.identity.repositoryRoot, boundaryCommit);
|
|
6206
|
+
if (!dependencyCommit)
|
|
6207
|
+
return null;
|
|
6208
|
+
boundaryCommit = dependencyCommit.parentCommitSha;
|
|
6209
|
+
dependencyCommitCount += 1;
|
|
6210
|
+
}
|
|
6211
|
+
if (dependencyCommitCount === 0)
|
|
6212
|
+
return null;
|
|
6213
|
+
const assessment = this.assessPostRebindCheckSupportDirtyCarryover(snapshot.identity.repositoryRoot, task, undefined, boundaryCommit);
|
|
6214
|
+
if (!assessment.eligible || !assessment.compatibility || !task.planHash)
|
|
6215
|
+
return null;
|
|
6216
|
+
return {
|
|
6217
|
+
...assessment.compatibility,
|
|
6218
|
+
planHash: task.planHash,
|
|
6219
|
+
mode: 'post-rebind-dirty-carryover',
|
|
6220
|
+
};
|
|
6221
|
+
}
|
|
6222
|
+
assessPostRebindCheckSupportDirtyCarryover(repositoryRoot, task, stepId, historyBoundary = headCommit(repositoryRoot)) {
|
|
6223
|
+
const relevantRecords = [...(task.dependencyProvenanceRecoveries ?? [])]
|
|
6224
|
+
.reverse()
|
|
6225
|
+
.filter((record) => {
|
|
6226
|
+
const compatibility = record.planIntegrity;
|
|
6227
|
+
return compatibility
|
|
6228
|
+
&& ['post-rebind-scope-adoption', 'post-rebind-dirty-carryover'].includes(compatibility.mode ?? '')
|
|
6229
|
+
&& (!stepId || compatibility.stepId === stepId);
|
|
6230
|
+
});
|
|
6231
|
+
if (relevantRecords.length === 0) {
|
|
6232
|
+
return {
|
|
6233
|
+
applicable: false,
|
|
6234
|
+
eligible: false,
|
|
6235
|
+
compatibility: null,
|
|
6236
|
+
dependencyRecoveryCommit: null,
|
|
6237
|
+
blockers: [],
|
|
6238
|
+
};
|
|
6239
|
+
}
|
|
6240
|
+
const blockers = [];
|
|
6241
|
+
const record = relevantRecords[0];
|
|
6242
|
+
const compatibility = record.planIntegrity;
|
|
6243
|
+
const step = task.steps.find((candidate) => candidate.id === compatibility.stepId) ?? null;
|
|
6244
|
+
const dirtyFiles = changedFiles(repositoryRoot).sort();
|
|
6245
|
+
let dirtyWorktreeHash = null;
|
|
6246
|
+
try {
|
|
6247
|
+
dirtyWorktreeHash = hashDirtyWorktree(repositoryRoot, dirtyFiles);
|
|
6248
|
+
}
|
|
6249
|
+
catch (error) {
|
|
6250
|
+
blockers.push(error instanceof Error ? error.message : String(error));
|
|
6251
|
+
}
|
|
6252
|
+
if (record.commitSha !== historyBoundary || !task.systemCommits.includes(record.commitSha)) {
|
|
6253
|
+
blockers.push('The latest check-support compatibility record must bind the exact registered history boundary.');
|
|
6254
|
+
}
|
|
6255
|
+
if (task.workspaceOwner !== 'local') {
|
|
6256
|
+
blockers.push('Check-support dirty carryover requires the recorded local Task workspace.');
|
|
6257
|
+
}
|
|
6258
|
+
if (!task.taskBranch || currentBranch(repositoryRoot) !== task.taskBranch) {
|
|
6259
|
+
blockers.push('Current branch must equal the recorded Task branch.');
|
|
6260
|
+
}
|
|
6261
|
+
if (!['ready', 'in_progress'].includes(task.status)) {
|
|
6262
|
+
blockers.push('The Task must retain an execution-authorized posture before dirty carryover.');
|
|
6263
|
+
}
|
|
6264
|
+
if (!task.planHash) {
|
|
6265
|
+
blockers.push('The Task must retain a current Plan hash.');
|
|
6266
|
+
}
|
|
6267
|
+
if (!step || step.status !== 'planned' || step.evidence !== null) {
|
|
6268
|
+
blockers.push(`The bound Step ${compatibility.stepId} must remain planned with no evidence.`);
|
|
6269
|
+
}
|
|
6270
|
+
if (task.steps.some((candidate) => candidate.status === 'in_progress')) {
|
|
6271
|
+
blockers.push('No Step may already be in progress before dirty carryover.');
|
|
6272
|
+
}
|
|
6273
|
+
if (canonicalJsonStringify(dirtyFiles)
|
|
6274
|
+
!== canonicalJsonStringify([...compatibility.dirtyFiles].sort())) {
|
|
6275
|
+
blockers.push('Dirty files changed after the registered check-support compatibility boundary.');
|
|
6276
|
+
}
|
|
6277
|
+
if (dirtyWorktreeHash !== compatibility.dirtyWorktreeHash) {
|
|
6278
|
+
blockers.push('Dirty file content changed after the registered check-support compatibility boundary.');
|
|
6279
|
+
}
|
|
6280
|
+
if (!compatibility.scopeExtensions?.length) {
|
|
6281
|
+
blockers.push('The compatibility record must retain at least one machine-derived scope extension.');
|
|
6282
|
+
}
|
|
6283
|
+
if (step) {
|
|
6284
|
+
if (dirtyFiles.some((dirtyPath) => !pathAllowed(dirtyPath, step.allowedWrites))) {
|
|
6285
|
+
blockers.push('The current Step must explicitly own every preserved dirty file.');
|
|
6286
|
+
}
|
|
6287
|
+
if ((compatibility.scopeExtensions ?? []).some((scopePath) => !pathAllowed(scopePath, step.allowedWrites)
|
|
6288
|
+
|| !step.expectedOutputs.some((declared) => (declared.startsWith('path:') ? declared.slice(5) : declared) === scopePath))) {
|
|
6289
|
+
blockers.push('The current Step must explicitly own every check-support scope extension as allowedWrites and expectedOutputs.');
|
|
6290
|
+
}
|
|
6291
|
+
}
|
|
6292
|
+
let reboundPlanHash = compatibility.planHash;
|
|
6293
|
+
for (const rebind of task.knowledgeRebinds) {
|
|
6294
|
+
if (rebind.previousPlanHash === reboundPlanHash)
|
|
6295
|
+
reboundPlanHash = rebind.reboundPlanHash;
|
|
6296
|
+
}
|
|
6297
|
+
if (!task.planHash || reboundPlanHash !== task.planHash) {
|
|
6298
|
+
blockers.push('The current Plan must equal the compatibility Plan or its exact Knowledge-rebind descendant.');
|
|
6299
|
+
}
|
|
6300
|
+
const matchingDecisions = readCorrectiveDecisionEvents(this.store, task).filter((event) => {
|
|
6301
|
+
const evidence = event.planIntegrityEvidence;
|
|
6302
|
+
return event.stepId === compatibility.stepId
|
|
6303
|
+
&& event.decision === 'continue-fix'
|
|
6304
|
+
&& event.recoveryBasis === 'plan-integrity'
|
|
6305
|
+
&& evidence?.kind === 'check-support-anchor-outside-allowed-writes'
|
|
6306
|
+
&& evidence.conflictHash === compatibility.conflictHash
|
|
6307
|
+
&& canonicalJsonStringify([...evidence.changedFiles].sort())
|
|
6308
|
+
=== canonicalJsonStringify([...compatibility.dirtyFiles].sort())
|
|
6309
|
+
&& canonicalJsonStringify(evidence.remediationEvents.map((binding) => binding.eventId))
|
|
6310
|
+
=== canonicalJsonStringify(compatibility.remediationEventIds)
|
|
6311
|
+
&& canonicalJsonStringify([...evidence.scopeExtensions].sort())
|
|
6312
|
+
=== canonicalJsonStringify([...(compatibility.scopeExtensions ?? [])].sort());
|
|
6313
|
+
});
|
|
6314
|
+
if (matchingDecisions.length !== 1) {
|
|
6315
|
+
blockers.push('Exactly one validated historical check-support decision must bind the preserved dirty set and scope.');
|
|
6316
|
+
}
|
|
6317
|
+
try {
|
|
6318
|
+
validateTaskHistory(repositoryRoot, task, historyBoundary);
|
|
6319
|
+
}
|
|
6320
|
+
catch (error) {
|
|
6321
|
+
blockers.push(error instanceof Error ? error.message : String(error));
|
|
6322
|
+
}
|
|
6323
|
+
return {
|
|
6324
|
+
applicable: true,
|
|
6325
|
+
eligible: blockers.length === 0,
|
|
6326
|
+
compatibility: blockers.length === 0
|
|
6327
|
+
? {
|
|
6328
|
+
...structuredClone(compatibility),
|
|
6329
|
+
planHash: task.planHash,
|
|
6330
|
+
dirtyFiles,
|
|
6331
|
+
dirtyWorktreeHash: dirtyWorktreeHash,
|
|
6332
|
+
mode: 'post-rebind-dirty-carryover',
|
|
6333
|
+
}
|
|
6334
|
+
: null,
|
|
6335
|
+
dependencyRecoveryCommit: record.commitSha,
|
|
6336
|
+
blockers,
|
|
6337
|
+
};
|
|
6338
|
+
}
|
|
6136
6339
|
inspectDependencyProvenanceCandidate(snapshot, task, activeDownstreamProof = null, downstreamProofReplan = null, historicalStepProvenance = null, planIntegrity = null) {
|
|
6137
6340
|
const versions = inspectWorkflowVersions(snapshot.identity.repositoryRoot, [task.baseBranch]);
|
|
6138
6341
|
const now = this.now().getTime();
|