@zq-silk/yui 0.15.11 → 0.16.0
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/ARCHITECTURE.md +11 -6
- package/ARCHITECTURE.zh-CN.md +8 -4
- package/README.md +13 -5
- package/dist/agentRun/agentRun.js +3 -0
- package/dist/artifacts/artifactCommitLock.js +16 -55
- package/dist/artifacts/taskArtifactRepository.js +3 -3
- package/dist/cli/agentConfigurationPicker.js +1 -1
- package/dist/cli/commandCatalog.js +92 -48
- package/dist/cli/completion.js +18 -18
- package/dist/cli/completionWizard.js +1 -1
- package/dist/cli/dynamicCompletion.js +1 -1
- package/dist/cli/interactionPolicy.js +11 -3
- package/dist/cli/invocationAuthority.js +64 -0
- package/dist/cli/managedDiagnostics.js +2 -2
- package/dist/cli/parseRepeatable.js +35 -0
- package/dist/cli/updatePorts.js +26 -5
- package/dist/cli.js +1303 -1235
- package/dist/commands/agentCommands.js +4 -4
- package/dist/commands/capabilityCommands.js +1 -1
- package/dist/commands/configCommands.js +6 -57
- package/dist/commands/configOverview.js +2 -2
- package/dist/commands/durableJobCommands.js +12 -6
- package/dist/commands/executionAuditCommands.js +10 -0
- package/dist/commands/globalRoleCommands.js +78 -29
- package/dist/commands/grantCommands.js +0 -3
- package/dist/commands/jobCommands.js +1 -4
- package/dist/commands/operatorCommands.js +3 -3
- package/dist/commands/projectCommands.js +45 -25
- package/dist/commands/resourcesCommands.js +10 -40
- package/dist/commands/roleConfiguration.js +2 -6
- package/dist/commands/taskActivationCommands.js +4 -8
- package/dist/commands/taskAgentCapabilities.js +9 -0
- package/dist/commands/taskCommandSupport.js +155 -0
- package/dist/commands/taskCommandTypes.js +1 -0
- package/dist/commands/taskCommands.js +355 -732
- package/dist/commands/taskCompletionGate.js +1 -1
- package/dist/commands/taskExecutionCommands.js +1 -1
- package/dist/commands/taskFactCommands.js +325 -0
- package/dist/commands/taskInputCommands.js +12 -42
- package/dist/commands/taskIntegrationCommands.js +20 -58
- package/dist/commands/taskNextActionCommand.js +7 -6
- package/dist/commands/taskPublicationAdoptCommand.js +127 -0
- package/dist/commands/taskPublicationCommands.js +12 -3
- package/dist/commands/taskPublicationVerifyCommand.js +24 -40
- package/dist/commands/taskRemoteDeliveryCommand.js +5 -86
- package/dist/commands/taskUpstreamCommands.js +19 -8
- package/dist/commands/workflowCommands.js +6 -1
- package/dist/completion/completionInstaller.js +26 -26
- package/dist/completion/completionState.js +26 -26
- package/dist/completion/fileCompletionManager.js +6 -11
- package/dist/config/configCatalog.js +0 -2
- package/dist/config/timeZone.js +14 -0
- package/dist/config/yuiConfig.js +0 -29
- package/dist/context/runContextPack.js +5 -3
- package/dist/context/taskCatalog.js +185 -0
- package/dist/context/taskContext.js +152 -151
- package/dist/context/wakeRunReferences.js +11 -0
- package/dist/controller/agentCapabilities.js +58 -0
- package/dist/controller/agentRuntimeObserver.js +6 -11
- package/dist/controller/clientRuntime.js +22 -14
- package/dist/controller/controller.js +20 -24
- package/dist/controller/fileSchedulerStoreAdapter.js +191 -225
- package/dist/controller/globalInputDelivery.js +13 -0
- package/dist/controller/jobClient.js +4 -4
- package/dist/controller/jobControl.js +60 -25
- package/dist/controller/jobSupervisor.js +4 -4
- package/dist/controller/providerRetryAdmission.js +100 -0
- package/dist/controller/providerRetryDelivery.js +218 -0
- package/dist/controller/runtime.js +85 -43
- package/dist/controller/runtimeEventProcessor.js +0 -5
- package/dist/controller/sessionOwnerReconciliation.js +5 -0
- package/dist/controller/taskAgentError.js +35 -0
- package/dist/coordination/workMailboxQueue.js +2 -24
- package/dist/core/controllerClient.js +2 -2
- package/dist/core/fileLockOwner.js +74 -0
- package/dist/decision/decision.js +17 -0
- package/dist/doctor/doctor.js +18 -9
- package/dist/event/taskEvent.js +12 -0
- package/dist/execution/workItemExecutionProjection.js +2 -2
- package/dist/executor/agentCapabilityConfig.js +43 -0
- package/dist/executor/agentConfigurationCatalog.js +49 -11
- package/dist/executor/agentExecutor.js +5 -8
- package/dist/executor/fileRoleLaunchPlanner.js +11 -53
- package/dist/executor/taskAgentCapabilities.js +64 -0
- package/dist/integration/deliveryObligation.js +1 -69
- package/dist/integration/gitIntegrationService.js +79 -90
- package/dist/integration/integrationAttempt.js +3 -14
- package/dist/integration/integrationSourceApplication.js +4 -12
- package/dist/integration/manifestTags.js +2 -2
- package/dist/job/durableJob.js +4 -25
- package/dist/job/jobAssignmentScope.js +22 -0
- package/dist/job/jobOperation.js +16 -0
- package/dist/job/jobRunner.js +2 -1
- package/dist/job/stepDirectory.js +15 -0
- package/dist/kernel/builtinCapabilities.js +15 -11
- package/dist/kernel/kernelPorts.js +1 -17
- package/dist/lifecycle/exactRunTerminalization.js +5 -10
- package/dist/message/globalProviderRetry.js +15 -0
- package/dist/message/inputControlResolution.js +3 -4
- package/dist/message/message.js +17 -20
- package/dist/message/messageContinuation.js +1 -1
- package/dist/milestone/milestone.js +11 -0
- package/dist/observability/executionAudit.js +19 -0
- package/dist/observability/orchestrationMetrics.js +24 -1
- package/dist/observability/runtimeIdentity.js +1 -17
- package/dist/output/agentConfigurationPresentation.js +2 -0
- package/dist/output/timePresentation.js +1 -14
- package/dist/plugins/pluginService.js +12 -4
- package/dist/release/releaseWorkflowPorts.js +45 -96
- package/dist/release/runtimeRelease.js +9 -15
- package/dist/repository/gitWorkspace.js +371 -105
- package/dist/repository/projectMaintenanceLock.js +98 -82
- package/dist/repository/taskBaseFreshness.js +1 -1
- package/dist/repository/taskWorkspaceCoordinator.js +82 -144
- package/dist/repository/taskWorkspacePreparer.js +113 -47
- package/dist/repository/workspaceCleanupInspection.js +187 -0
- package/dist/resources/autoResourceGc.js +4 -77
- package/dist/resources/liveReferences.js +7 -54
- package/dist/resources/resourceDiscovery.js +5 -118
- package/dist/resources/resourceGc.js +233 -181
- package/dist/resources/resourceRegistrar.js +5 -4
- package/dist/resources/resourceRegistry.js +3 -37
- package/dist/resources/resourceRegistryStore.js +13 -36
- package/dist/resources/sqliteResourceRegistry.js +43 -26
- package/dist/review/deltaRecheck.js +1 -1
- package/dist/review/reviewAcceptance.js +1 -1
- package/dist/review/reviewDecision.js +1 -1
- package/dist/review/reviewRound.js +8 -7
- package/dist/review/taskFinalReviewContractResolution.js +1 -1
- package/dist/runtime/acpProtocol.js +4 -51
- package/dist/runtime/acpSession.js +10 -75
- package/dist/runtime/acpSessionConfiguration.js +1 -8
- package/dist/runtime/agentEndpoint.js +3 -2
- package/dist/runtime/agentError.js +5 -3
- package/dist/runtime/agentFailureContext.js +43 -0
- package/dist/runtime/agentHost.js +42 -25
- package/dist/runtime/builtinAgentErrorMappers.js +91 -0
- package/dist/runtime/codexAppServerRuntime.js +34 -3
- package/dist/runtime/codexInteractiveHost.js +2 -2
- package/dist/runtime/index.js +0 -1
- package/dist/runtime/managedCaller.js +21 -1
- package/dist/runtime/providerControl.js +5 -1
- package/dist/runtime/providerErrors.js +38 -0
- package/dist/runtime/providerRetry.js +198 -0
- package/dist/runtime/providerRuntimeIdentity.js +28 -2
- package/dist/runtime/runtimeObservation.js +0 -5
- package/dist/runtime/runtimeSessionCandidate.js +2 -3
- package/dist/runtime/sessionOwnerIdentity.js +1 -1
- package/dist/runtime/sessionTokenMetrics.js +15 -5
- package/dist/runtime/structuredProviderHost.js +10 -43
- package/dist/runtime/taskUsageMetrics.js +275 -0
- package/dist/runtime/tmuxAdapters.js +6 -8
- package/dist/scheduler/activeRoleRunDelivery.js +19 -7
- package/dist/scheduler/leaderWakeupProcessor.js +24 -6
- package/dist/scheduler/operatorEvent.js +12 -20
- package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
- package/dist/scheduler/ports.js +5 -8
- package/dist/scheduler/roleRunLiveness.js +4 -4
- package/dist/scheduler/roleRunStall.js +16 -23
- package/dist/scheduler/taskExecutionProjection.js +49 -36
- package/dist/scheduler/taskObservabilityProjection.js +6 -45
- package/dist/scheduler/taskWake.js +5 -10
- package/dist/scheduler/wakeReason.js +2 -0
- package/dist/scheduler/wakeupQueue.js +2 -4
- package/dist/setup/setupCommand.js +1 -1
- package/dist/storage/contextRecords.js +106 -0
- package/dist/storage/currentTaskStore.js +1 -1
- package/dist/storage/homeLayout.js +13 -17
- package/dist/storage/migrations/agentFailureContext.js +22 -0
- package/dist/storage/migrations/currentInputContract.js +86 -0
- package/dist/storage/migrations/currentRuntimeContract.js +228 -0
- package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
- package/dist/storage/migrations/integrationContinuation.js +5 -4
- package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
- package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
- package/dist/storage/migrations/verificationPlanV1.js +162 -0
- package/dist/storage/migrations/verificationPolicy.js +74 -0
- package/dist/storage/migrations/workItemHistory.js +46 -0
- package/dist/storage/persistenceWorker.js +12 -4
- package/dist/storage/recordValidation.js +87 -0
- package/dist/storage/sqliteSchema.js +173 -1
- package/dist/storage/sqliteStore.js +172 -161
- package/dist/storage/storageSchema.js +2 -14
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +16 -7
- package/dist/storage/taskCatalog.js +123 -0
- package/dist/storage/taskStore.js +5 -7
- package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
- package/dist/task/archiveDiagnostics.js +1 -0
- package/dist/task/archivePreflight.js +124 -0
- package/dist/task/completionReadiness.js +5 -24
- package/dist/task/draftPlan.js +0 -53
- package/dist/task/nextAction.js +7 -13
- package/dist/task/publicationAdoption.js +56 -0
- package/dist/task/publicationReference.js +10 -0
- package/dist/task/remoteDelivery.js +31 -16
- package/dist/task/remoteDeliveryService.js +89 -0
- package/dist/task/taskActivation.js +2 -25
- package/dist/task/taskActivationService.js +0 -2
- package/dist/task/taskRecordReference.js +0 -1
- package/dist/task/taskSubmission.js +3 -14
- package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
- package/dist/telemetry/sqliteTelemetryStore.js +49 -49
- package/dist/telemetry/telemetryWiring.js +4 -3
- package/dist/tmux/tmuxManager.js +29 -20
- package/dist/verification/gateArtifact.js +15 -24
- package/dist/verification/gateArtifactStore.js +14 -7
- package/dist/verification/verificationGateService.js +29 -88
- package/dist/verification/verificationPlan.js +27 -72
- package/dist/web/assets/client/app.js +92 -17
- package/dist/web/assets/client/components.js +55 -13
- package/dist/web/assets/client/i18n.js +72 -4
- package/dist/web/assets/client/taskSurface.js +6 -5
- package/dist/web/assets/client/view.js +35 -7
- package/dist/web/assets/shell.js +6 -0
- package/dist/web/assets/styles/layout.js +7 -0
- package/dist/web/webServer.js +12 -3
- package/dist/web/webSnapshot.js +14 -85
- package/dist/web/webTaskSurface.js +17 -46
- package/dist/workItem/workItem.js +1 -12
- package/dist/workspace/cleanupInspection.js +63 -0
- package/dist/workspace/workItemChangeSetManager.js +110 -50
- package/docs/agent-result-consumption.md +4 -0
- package/docs/agent-result-consumption.zh-CN.md +3 -0
- package/docs/agent-runtime-drivers.md +7 -0
- package/docs/agent-runtime-drivers.zh-CN.md +5 -0
- package/docs/architecture/README.md +2 -0
- package/docs/architecture/README.zh-CN.md +3 -1
- package/docs/architecture/capabilities-and-resources.md +42 -5
- package/docs/architecture/capabilities-and-resources.zh-CN.md +33 -3
- package/docs/managed-turn-and-session-runtime.md +121 -1
- package/docs/managed-turn-and-session-runtime.zh-CN.md +96 -2
- package/docs/observability/README.md +62 -0
- package/docs/observability/README.zh-CN.md +47 -0
- package/docs/plugin-sdk.md +4 -2
- package/docs/project-refresh.md +77 -0
- package/docs/project-refresh.zh-CN.md +59 -0
- package/docs/provider-retry.md +70 -0
- package/docs/provider-runtime.md +3 -1
- package/docs/provider-runtime.zh-CN.md +4 -2
- package/docs/release-workflow.md +190 -7
- package/docs/release-workflow.zh-CN.md +139 -5
- package/docs/roles-and-configuration.md +29 -0
- package/docs/roles-and-configuration.zh-CN.md +21 -0
- package/docs/sqlite-control-plane-design.md +16 -3
- package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
- package/docs/task-dag-semantics.md +3 -3
- package/docs/task-dag-semantics.zh-CN.md +2 -2
- package/docs/task-delivery.md +223 -17
- package/docs/task-delivery.zh-CN.md +171 -14
- package/docs/task-discovery.md +101 -0
- package/docs/task-discovery.zh-CN.md +85 -0
- package/docs/testing/verification-levels.md +161 -9
- package/docs/testing/verification-levels.zh-CN.md +116 -9
- package/i18n/README.zh-CN.md +13 -7
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +5 -0
- package/skills/yui-leader/references/execution.md +3 -2
- package/skills/yui-leader/references/integration.md +9 -5
- package/skills/yui-leader/references/planning.md +9 -3
- package/skills/yui-operator/SKILL.md +24 -8
- package/skills/yui-reviewer/SKILL.md +4 -0
- package/skills/yui-runtime/SKILL.md +17 -2
- package/skills/yui-runtime/references/publication.md +26 -4
- package/skills/yui-runtime/references/recovery.md +71 -0
- package/dist/agent/agentRegistry.js +0 -10
- package/dist/agentRun/runIdentity.js +0 -22
- package/dist/commands/deliveryGuardPreflight.js +0 -30
- package/dist/commands/taskIntegrationQueueCommands.js +0 -226
- package/dist/commands/taskOverviewCommand.js +0 -376
- package/dist/completion/completionWizard.js +0 -125
- package/dist/context/dispatchContext.js +0 -110
- package/dist/context/wakeNotification.js +0 -144
- package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
- package/dist/integration/integrationQueueEntry.js +0 -191
- package/dist/integration/integrationQueueService.js +0 -810
- package/dist/release/fakeReleasePorts.js +0 -55
- package/dist/runtime/sessionOwnerRegistry.js +0 -137
- package/dist/task/deliveryGuard.js +0 -226
- /package/dist/{commands/taskActor.js → task/taskAuthority.js} +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { readdir, rename, rm } from "node:fs/promises";
|
|
3
3
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
-
import { usageError } from "../errors/cliError.js";
|
|
4
|
+
import { CliError, usageError } from "../errors/cliError.js";
|
|
5
5
|
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
6
6
|
import { healCheckoutSwap, restoreCheckoutSwap, swapManagedCheckout } from "../repository/checkoutSwap.js";
|
|
7
|
-
import { NodeGitWorkspace } from "../repository/gitWorkspace.js";
|
|
7
|
+
import { GitWorkspaceRefreshError, NodeGitWorkspace } from "../repository/gitWorkspace.js";
|
|
8
8
|
import { acquireProjectMaintenanceLock } from "../repository/projectMaintenanceLock.js";
|
|
9
9
|
import { addProjectKnowledge, addKnowledgeProposal, assertProjectActive, decideKnowledgeProposal, createProject, findKnowledgeProposal, findKnowledgeProposalByFingerprint, knowledgeEvidenceDigest, knowledgeProposalFingerprint, managedProjectPath, planKnowledgeAcceptance, retireProject, retireProjectKnowledge, resolveProject, updateProjectKnowledge, updateProjectMetadata, validateProject, validateProjectName } from "../repository/project.js";
|
|
10
10
|
import { managedWorkspacesRoot } from "../storage/homeLayout.js";
|
|
11
|
-
import { projectActor } from "
|
|
11
|
+
import { projectActor } from "../task/taskAuthority.js";
|
|
12
12
|
export async function runProjectCommand(args, store, options = {}) {
|
|
13
13
|
const [command, ...rest] = args;
|
|
14
14
|
if (command === "add" || command === "clone") {
|
|
@@ -22,10 +22,14 @@ export async function runProjectCommand(args, store, options = {}) {
|
|
|
22
22
|
}
|
|
23
23
|
if (command === "refresh") {
|
|
24
24
|
const refreshed = await refreshProject(rest, store, options);
|
|
25
|
+
const head = refreshed.changed
|
|
26
|
+
? `Refreshed project ${refreshed.project.id}: ${refreshed.fromCommit} -> ${refreshed.toCommit}`
|
|
27
|
+
: `Project ${refreshed.project.id} HEAD is already current at ${refreshed.toCommit}`;
|
|
28
|
+
const tracking = refreshed.tracking;
|
|
25
29
|
return {
|
|
26
|
-
output:
|
|
27
|
-
? `
|
|
28
|
-
: `
|
|
30
|
+
output: `${head}\n` + (tracking.status === "unmanaged"
|
|
31
|
+
? `Tracking unmanaged: ${tracking.reason}\n`
|
|
32
|
+
: `Tracking ${tracking.status}: ${tracking.ref} at ${tracking.toCommit}\n`),
|
|
29
33
|
data: refreshed
|
|
30
34
|
};
|
|
31
35
|
}
|
|
@@ -101,14 +105,23 @@ async function refreshProject(args, store, options) {
|
|
|
101
105
|
// RFC Phase 1: hold the per-Project maintenance fence for the whole refresh
|
|
102
106
|
// so Task workspace preparation and other maintenance cannot interleave
|
|
103
107
|
// with the canonical branch/working-tree move.
|
|
104
|
-
const releaseMaintenance = acquireProjectMaintenanceLock(store.rootDirectory(), project.id);
|
|
108
|
+
const releaseMaintenance = await acquireProjectMaintenanceLock(store.rootDirectory(), project.id, { signal: options.signal });
|
|
105
109
|
try {
|
|
110
|
+
const current = requireUnchangedMaintenanceProject(store, project, "refresh");
|
|
106
111
|
const refreshed = await (options.git ?? new NodeGitWorkspace()).refresh({
|
|
107
|
-
repositoryPath:
|
|
108
|
-
remoteUrl:
|
|
109
|
-
stableRef:
|
|
112
|
+
repositoryPath: current.path,
|
|
113
|
+
remoteUrl: current.remoteUrl,
|
|
114
|
+
stableRef: current.stableBranch
|
|
110
115
|
});
|
|
111
|
-
return { project, ...refreshed };
|
|
116
|
+
return { project: current, ...refreshed };
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
if (error instanceof GitWorkspaceRefreshError) {
|
|
120
|
+
throw new CliError("RUNTIME_ERROR", error.message, undefined, {
|
|
121
|
+
projectId: project.id, refresh: error.result
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
throw error;
|
|
112
125
|
}
|
|
113
126
|
finally {
|
|
114
127
|
releaseMaintenance();
|
|
@@ -131,8 +144,9 @@ async function diagnoseProject(args, store, options) {
|
|
|
131
144
|
};
|
|
132
145
|
}
|
|
133
146
|
const git = options.git ?? new NodeGitWorkspace();
|
|
134
|
-
const releaseMaintenance = acquireProjectMaintenanceLock(store.rootDirectory(), project.id);
|
|
147
|
+
const releaseMaintenance = await acquireProjectMaintenanceLock(store.rootDirectory(), project.id, { signal: options.signal });
|
|
135
148
|
try {
|
|
149
|
+
requireUnchangedMaintenanceProject(store, project, "diagnose");
|
|
136
150
|
const current = await git.inspect(project.path, "HEAD");
|
|
137
151
|
const remote = await git.resolveRemoteBaseline({
|
|
138
152
|
repositoryPath: project.path,
|
|
@@ -307,13 +321,14 @@ async function migrateProject(args, store, options) {
|
|
|
307
321
|
// Migration rewrites the Project's Git repository: hold the per-Project
|
|
308
322
|
// maintenance fence so no rebuild/archive/cleanup (or a second migrate)
|
|
309
323
|
// interleaves, and the Controller defers worktree preparation meanwhile.
|
|
310
|
-
const releaseMaintenance = acquireProjectMaintenanceLock(store.rootDirectory(), project.id);
|
|
324
|
+
const releaseMaintenance = await acquireProjectMaintenanceLock(store.rootDirectory(), project.id, { signal: options.signal });
|
|
311
325
|
try {
|
|
312
326
|
// Re-read the Project under the fence. A concurrent migration may have
|
|
313
327
|
// completed and switched the catalog to the Home-managed repo since the
|
|
314
328
|
// record was resolved above; the stale snapshot must not drive any Git
|
|
315
329
|
// effect (in particular it must not delete the now-canonical repo).
|
|
316
330
|
const current = requireProject(store, project.id);
|
|
331
|
+
assertProjectActive(current, "migrate");
|
|
317
332
|
if (current.ownership === "managed") {
|
|
318
333
|
throw usageError(`Project is already Home-managed: ${project.id}.`);
|
|
319
334
|
}
|
|
@@ -588,7 +603,7 @@ async function resetProject(args, store, options) {
|
|
|
588
603
|
throw usageError(`Project reset requires matching stable and development branches: ${project.id}.`);
|
|
589
604
|
}
|
|
590
605
|
const git = options.git ?? new NodeGitWorkspace();
|
|
591
|
-
const releaseMaintenance = acquireProjectMaintenanceLock(store.rootDirectory(), project.id);
|
|
606
|
+
const releaseMaintenance = await acquireProjectMaintenanceLock(store.rootDirectory(), project.id, { signal: options.signal });
|
|
592
607
|
try {
|
|
593
608
|
// Re-read under the fence so a concurrent catalog change can never drive
|
|
594
609
|
// a destructive Git effect from a stale snapshot.
|
|
@@ -713,7 +728,7 @@ async function replaceProject(args, store, options) {
|
|
|
713
728
|
+ "Re-run with --discard-local to acknowledge that the checkout and any uncommitted state will be discarded.");
|
|
714
729
|
}
|
|
715
730
|
const git = options.git ?? new NodeGitWorkspace();
|
|
716
|
-
const releaseMaintenance = acquireProjectMaintenanceLock(store.rootDirectory(), project.id);
|
|
731
|
+
const releaseMaintenance = await acquireProjectMaintenanceLock(store.rootDirectory(), project.id, { signal: options.signal });
|
|
717
732
|
try {
|
|
718
733
|
const current = requireProject(store, project.id);
|
|
719
734
|
assertProjectActive(current, "replace");
|
|
@@ -922,8 +937,9 @@ async function deleteProjectCommand(args, store, options) {
|
|
|
922
937
|
// every failure restores it, so the catalog never loses its recoverable
|
|
923
938
|
// entry while a live checkout (or its failure) is still in play.
|
|
924
939
|
const tombstone = join(store.rootDirectory(), "projects", `.delete-${project.id}`);
|
|
925
|
-
const releaseMaintenance = acquireProjectMaintenanceLock(store.rootDirectory(), project.id);
|
|
940
|
+
const releaseMaintenance = await acquireProjectMaintenanceLock(store.rootDirectory(), project.id, { signal: options.signal });
|
|
926
941
|
try {
|
|
942
|
+
requireUnchangedMaintenanceProject(store, project, "delete");
|
|
927
943
|
// Heal a crashed earlier attempt before the prechecks turn.
|
|
928
944
|
await healCheckoutSwap({ currentPath: project.path, backupPath: tombstone });
|
|
929
945
|
if (existsSync(project.path)) {
|
|
@@ -1289,6 +1305,19 @@ function projectKnowledge(args, store, options) {
|
|
|
1289
1305
|
? "Project knowledge command is required."
|
|
1290
1306
|
: `Unknown command: project knowledge ${command}`);
|
|
1291
1307
|
}
|
|
1308
|
+
/** Waiting yields to catalog writers too; never apply stale Git/lifecycle inputs. */
|
|
1309
|
+
function requireUnchangedMaintenanceProject(store, expected, action) {
|
|
1310
|
+
const current = requireProject(store, expected.id);
|
|
1311
|
+
if (current.path !== expected.path
|
|
1312
|
+
|| current.remoteUrl !== expected.remoteUrl
|
|
1313
|
+
|| current.stableBranch !== expected.stableBranch
|
|
1314
|
+
|| current.developmentBranch !== expected.developmentBranch
|
|
1315
|
+
|| current.ownership !== expected.ownership
|
|
1316
|
+
|| current.status !== expected.status) {
|
|
1317
|
+
throw new Error(`Project changed while waiting to ${action}: ${expected.id}; read current state before retrying.`);
|
|
1318
|
+
}
|
|
1319
|
+
return current;
|
|
1320
|
+
}
|
|
1292
1321
|
function requireProject(store, reference) {
|
|
1293
1322
|
const project = resolveProject(store.listProjects(), reference);
|
|
1294
1323
|
if (project === null)
|
|
@@ -1468,9 +1497,6 @@ function proposeKnowledge(args, store, options) {
|
|
|
1468
1497
|
* digest at proposal time, so a later Agent can verify the source record.
|
|
1469
1498
|
*/
|
|
1470
1499
|
function requireProposalEvidence(store, parsed) {
|
|
1471
|
-
if (store.getTask === undefined) {
|
|
1472
|
-
throw usageError("Knowledge promotion requires Task evidence, which this store does not provide.");
|
|
1473
|
-
}
|
|
1474
1500
|
const task = store.getTask(parsed.task);
|
|
1475
1501
|
if (task === null)
|
|
1476
1502
|
throw usageError(`Task not found: ${parsed.task}.`);
|
|
@@ -1482,9 +1508,6 @@ function requireProposalEvidence(store, parsed) {
|
|
|
1482
1508
|
};
|
|
1483
1509
|
let evidence;
|
|
1484
1510
|
if (parsed.decision !== undefined) {
|
|
1485
|
-
if (store.getDecision === undefined) {
|
|
1486
|
-
throw usageError("Knowledge promotion requires Decision evidence, which this store does not provide.");
|
|
1487
|
-
}
|
|
1488
1511
|
const decision = store.getDecision(task.id, parsed.decision);
|
|
1489
1512
|
if (decision === null) {
|
|
1490
1513
|
throw usageError(`Decision not found: ${parsed.decision} in Task ${task.id}.`);
|
|
@@ -1492,9 +1515,6 @@ function requireProposalEvidence(store, parsed) {
|
|
|
1492
1515
|
evidence = `${decision.title}\n${decision.rationale}`;
|
|
1493
1516
|
}
|
|
1494
1517
|
else if (parsed.milestone !== undefined) {
|
|
1495
|
-
if (store.getMilestone === undefined) {
|
|
1496
|
-
throw usageError("Knowledge promotion requires Milestone evidence, which this store does not provide.");
|
|
1497
|
-
}
|
|
1498
1518
|
const milestone = store.getMilestone(task.id, parsed.milestone);
|
|
1499
1519
|
if (milestone === null) {
|
|
1500
1520
|
throw usageError(`Milestone not found: ${parsed.milestone} in Task ${task.id}.`);
|
|
@@ -7,12 +7,11 @@
|
|
|
7
7
|
* every quarantined resource to its original path.
|
|
8
8
|
*/
|
|
9
9
|
import { resolve } from "node:path";
|
|
10
|
+
import { resolveResourcesGcMode, resolveResourcesQuarantineTtlHours } from "../config/yuiConfig.js";
|
|
10
11
|
import { usageError } from "../errors/cliError.js";
|
|
11
12
|
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
12
|
-
import { applyResourceGc, planResourceGc, purgeResourceQuarantine, restoreAllResourceGc } from "../resources/resourceGc.js";
|
|
13
|
-
import { createResourceRegistryStore } from "../resources/resourceRegistryStore.js";
|
|
14
13
|
import { resourceKindLabel, resourceOwnerLabel } from "../resources/resourceDiscovery.js";
|
|
15
|
-
import {
|
|
14
|
+
import { applyResourceGc, planResourceGc, readResourceGcState, purgeResourceQuarantine, restoreAllResourceGc } from "../resources/resourceGc.js";
|
|
16
15
|
export async function runResourcesCommand(args, store, options = {}) {
|
|
17
16
|
const [command, ...rest] = args;
|
|
18
17
|
if (command !== "gc") {
|
|
@@ -27,10 +26,7 @@ async function runGcCommand(args, store, options) {
|
|
|
27
26
|
const now = options.now?.() ?? new Date();
|
|
28
27
|
const home = resolve(store.rootDirectory());
|
|
29
28
|
const mode = resolveGcMode(store);
|
|
30
|
-
const projects = store
|
|
31
|
-
const managedWorkspaces = collectManagedWorkspaces(store);
|
|
32
|
-
const taskStatusById = collectTaskStatuses(store);
|
|
33
|
-
const activeWorkspaceOwnerPaths = collectActiveWorkspaceOwnerPaths(store);
|
|
29
|
+
const { projects, managedWorkspaces, taskStatusById, activeWorkspaceOwnerPaths, sessionOwners } = readResourceGcState(store);
|
|
34
30
|
if (action === "restore") {
|
|
35
31
|
const result = await restoreAllResourceGc(home, { now });
|
|
36
32
|
return {
|
|
@@ -46,7 +42,7 @@ async function runGcCommand(args, store, options) {
|
|
|
46
42
|
data: { mode, action: "purge", skipped: true }
|
|
47
43
|
};
|
|
48
44
|
}
|
|
49
|
-
const result = await purgeResourceQuarantine(home, { now, ttlHours, managedWorkspaces });
|
|
45
|
+
const result = await purgeResourceQuarantine(home, { now, ttlHours, managedWorkspaces }, store);
|
|
50
46
|
return {
|
|
51
47
|
output: renderPurgeResult(result),
|
|
52
48
|
data: result
|
|
@@ -54,7 +50,7 @@ async function runGcCommand(args, store, options) {
|
|
|
54
50
|
}
|
|
55
51
|
const plan = await planResourceGc({
|
|
56
52
|
home,
|
|
57
|
-
|
|
53
|
+
sessionOwners,
|
|
58
54
|
projects,
|
|
59
55
|
managedWorkspaces,
|
|
60
56
|
taskStatusById,
|
|
@@ -66,7 +62,7 @@ async function runGcCommand(args, store, options) {
|
|
|
66
62
|
if (action === "apply" && mode === "quarantine") {
|
|
67
63
|
const result = await applyResourceGc({
|
|
68
64
|
home,
|
|
69
|
-
|
|
65
|
+
sessionOwners,
|
|
70
66
|
projects,
|
|
71
67
|
managedWorkspaces,
|
|
72
68
|
taskStatusById,
|
|
@@ -74,7 +70,7 @@ async function runGcCommand(args, store, options) {
|
|
|
74
70
|
now,
|
|
75
71
|
quarantineTtlHours: ttlHours,
|
|
76
72
|
activeWorkspaceOwnerPaths
|
|
77
|
-
}, plan);
|
|
73
|
+
}, plan, store);
|
|
78
74
|
return {
|
|
79
75
|
output: renderApplyResult(result),
|
|
80
76
|
data: result
|
|
@@ -109,35 +105,6 @@ function parseTtlHours(args, store) {
|
|
|
109
105
|
function resolveGcMode(store) {
|
|
110
106
|
return resolveResourcesGcMode(store.getConfig().resourcesGcMode);
|
|
111
107
|
}
|
|
112
|
-
function collectManagedWorkspaces(store) {
|
|
113
|
-
const workspaces = [];
|
|
114
|
-
for (const task of store.listTasks()) {
|
|
115
|
-
workspaces.push(...store.listManagedWorkspaces(task.id));
|
|
116
|
-
}
|
|
117
|
-
return workspaces;
|
|
118
|
-
}
|
|
119
|
-
function collectTaskStatuses(store) {
|
|
120
|
-
const statuses = new Map();
|
|
121
|
-
for (const task of store.listTasks()) {
|
|
122
|
-
statuses.set(task.id, task.status);
|
|
123
|
-
}
|
|
124
|
-
return statuses;
|
|
125
|
-
}
|
|
126
|
-
/** Workspace paths claimed by active durable Jobs (AgentRuns). */
|
|
127
|
-
function collectActiveWorkspaceOwnerPaths(store) {
|
|
128
|
-
const paths = [];
|
|
129
|
-
for (const task of store.listTasks()) {
|
|
130
|
-
for (const run of store.listRuns(task.id)) {
|
|
131
|
-
if (run.status !== "active")
|
|
132
|
-
continue;
|
|
133
|
-
const workspace = run.workspace;
|
|
134
|
-
if (workspace === undefined)
|
|
135
|
-
continue;
|
|
136
|
-
paths.push(workspace.root, ...workspace.entries.map((entry) => entry.path));
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return paths;
|
|
140
|
-
}
|
|
141
108
|
function renderPlan(plan, action) {
|
|
142
109
|
const lines = [];
|
|
143
110
|
const modeNotice = plan.mode === "report" && action === "apply"
|
|
@@ -207,6 +174,9 @@ function renderApplyResult(result) {
|
|
|
207
174
|
if (result.restored.length > 0) {
|
|
208
175
|
lines.push(`Restored ${result.restored.length} resource(s) from quarantine.`);
|
|
209
176
|
}
|
|
177
|
+
for (const record of result.planned.retained) {
|
|
178
|
+
lines.push(`Retained ${record.id} ${record.path}: ${record.blocker ?? "current ownership or live reference"}`);
|
|
179
|
+
}
|
|
210
180
|
if (result.applied.length === 0 && result.failed.length === 0 && result.restored.length === 0) {
|
|
211
181
|
lines.push("No releasable resources.");
|
|
212
182
|
}
|
|
@@ -187,12 +187,8 @@ function permissionPatch(binding, parsed) {
|
|
|
187
187
|
}
|
|
188
188
|
const current = binding.config.permission;
|
|
189
189
|
if (selected !== "configured" && !nativeOptions) {
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
// permission at all, and it must be the adapter's own default rather than a
|
|
193
|
-
// fixed `bypass`: ACP defaults to `default`, and defaulting it to bypass
|
|
194
|
-
// here would widen authority the user never granted.
|
|
195
|
-
return structuredClone(current ?? defaultRoleAgentConfig(binding.adapterId).permission);
|
|
190
|
+
// The validated binding always has its explicit permission strategy.
|
|
191
|
+
return structuredClone(current);
|
|
196
192
|
}
|
|
197
193
|
const permission = current?.strategy === "configured"
|
|
198
194
|
? structuredClone(current)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { usageError } from "../errors/cliError.js";
|
|
3
|
-
import { currentManagedRuntime } from "../runtime/managedCaller.js";
|
|
4
2
|
import { enqueueWork } from "../coordination/workMailboxQueue.js";
|
|
3
|
+
import { usageError } from "../errors/cliError.js";
|
|
5
4
|
import { SYSTEM_LEADER_ROLE } from "../role/systemRoles.js";
|
|
5
|
+
import { currentManagedRuntime } from "../runtime/managedCaller.js";
|
|
6
6
|
import { describeEnvironmentPlan } from "../task/taskActivation.js";
|
|
7
7
|
import { cancelTaskActivation, requestTaskActivation, taskActivationOperationRef } from "../task/taskActivationService.js";
|
|
8
|
-
import { taskLocalActor } from "
|
|
8
|
+
import { taskLocalActor } from "../task/taskAuthority.js";
|
|
9
9
|
/**
|
|
10
10
|
* Explicit Activation requests.
|
|
11
11
|
*
|
|
@@ -52,10 +52,6 @@ function requestActivation(args, store, options) {
|
|
|
52
52
|
actorId: caller.actorId,
|
|
53
53
|
authorityRef: caller.authorityRef,
|
|
54
54
|
environmentPlan,
|
|
55
|
-
// The activation-request command is the explicit activation boundary, so
|
|
56
|
-
// every request it records carries an explicit provable origin (task-32
|
|
57
|
-
// §2.4). A develop submission records its own request with `submit-develop`.
|
|
58
|
-
origin: "explicit",
|
|
59
55
|
...(caller.planningRunId === undefined ? {} : { callerRunId: caller.planningRunId })
|
|
60
56
|
}, now));
|
|
61
57
|
// An immediate request has nothing left to wait for, so ask the Controller to
|
|
@@ -65,7 +61,7 @@ function requestActivation(args, store, options) {
|
|
|
65
61
|
store.transaction((tx) => {
|
|
66
62
|
enqueueWork(tx, { kind: "task", taskId }, "activation-requested", now, [{ type: "task", id: taskId }]);
|
|
67
63
|
});
|
|
68
|
-
void options.runtime?.notifyMailboxChanged
|
|
64
|
+
void options.runtime?.notifyMailboxChanged({ kind: "task", taskId });
|
|
69
65
|
}
|
|
70
66
|
const request = result.request;
|
|
71
67
|
const lines = [
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { exactPositionals, parseTail } from "./taskCommandSupport.js";
|
|
2
|
+
export function parseTaskAgentCapabilityQuery(args) {
|
|
3
|
+
const usage = "Usage: yui task role capabilities <task> <role> [--error <event-id>] [--refresh]";
|
|
4
|
+
const parsed = parseTail(args, new Set(["--error"]), usage, new Set(["--refresh"]));
|
|
5
|
+
exactPositionals(parsed.positionals, 2, usage);
|
|
6
|
+
const errorId = parsed.options.get("--error");
|
|
7
|
+
return { taskId: parsed.positionals[0], roleName: parsed.positionals[1],
|
|
8
|
+
...(errorId === undefined ? {} : { errorId }), refresh: parsed.options.has("--refresh") };
|
|
9
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { taskNotFound, usageError } from "../errors/cliError.js";
|
|
2
|
+
import { createTaskEvent } from "../event/taskEvent.js";
|
|
3
|
+
import { formatTimestamp } from "../output/timePresentation.js";
|
|
4
|
+
import { SYSTEM_LEADER_ROLE as LEADER_ROLE } from "../role/systemRoles.js";
|
|
5
|
+
import { currentManagedRuntime } from "../runtime/managedCaller.js";
|
|
6
|
+
import { taskLocalActor as resolveTaskLocalActor } from "../task/taskAuthority.js";
|
|
7
|
+
export function recordTaskEvent(store, taskId, type, payload, now) {
|
|
8
|
+
return recordTaskEventRecord(store, taskId, type, payload, now);
|
|
9
|
+
}
|
|
10
|
+
export function leaderActionEventPayload(store, taskId, options) {
|
|
11
|
+
const caller = currentManagedRuntime(store, options.environment, taskId, "leader");
|
|
12
|
+
// A long-lived Session may be handling direct input while another request
|
|
13
|
+
// awaits admission. The Role's active pointer cannot prove command origin.
|
|
14
|
+
return caller === undefined ? {} : { leaderNativeSessionId: caller.nativeSessionId };
|
|
15
|
+
}
|
|
16
|
+
export function recordTaskEventRecord(store, taskId, type, payload, now) {
|
|
17
|
+
const event = createTaskEvent(store.nextEventId(taskId), taskId, type, payload, now);
|
|
18
|
+
store.saveEvent(taskId, event);
|
|
19
|
+
return event;
|
|
20
|
+
}
|
|
21
|
+
export function requireTask(store, taskId) {
|
|
22
|
+
const id = requiredText(taskId, "Task id");
|
|
23
|
+
const task = store.getTask(id);
|
|
24
|
+
if (task === null)
|
|
25
|
+
throw taskNotFound(id);
|
|
26
|
+
return task;
|
|
27
|
+
}
|
|
28
|
+
export function assertTaskOpen(task) {
|
|
29
|
+
if (task.status === "completed") {
|
|
30
|
+
throw usageError(`Task ${task.id} is completed; reopen it before continuing.`);
|
|
31
|
+
}
|
|
32
|
+
if (task.status === "archived")
|
|
33
|
+
throw usageError(`Task is archived: ${task.id}.`);
|
|
34
|
+
if (task.status === "cancelled")
|
|
35
|
+
throw usageError(`Task is retired: ${task.id}.`);
|
|
36
|
+
}
|
|
37
|
+
export function taskActor(store, options, taskId) {
|
|
38
|
+
return resolveTaskLocalActor(store, options.environment, taskId);
|
|
39
|
+
}
|
|
40
|
+
export function parseMultiValueTail(args, valueOptions, repeatOptions, usage, flagOptions = new Set()) {
|
|
41
|
+
const positionals = [];
|
|
42
|
+
const options = new Map();
|
|
43
|
+
const multiOptions = new Map();
|
|
44
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
45
|
+
const value = args[index];
|
|
46
|
+
if (!value.startsWith("--")) {
|
|
47
|
+
positionals.push(value);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (!valueOptions.has(value) && !repeatOptions.has(value) && !flagOptions.has(value)) {
|
|
51
|
+
throw usageError(`Unsupported option: ${value}.`, usage);
|
|
52
|
+
}
|
|
53
|
+
if (flagOptions.has(value)) {
|
|
54
|
+
if (options.has(value))
|
|
55
|
+
throw usageError(`Option may only be specified once: ${value}.`, usage);
|
|
56
|
+
options.set(value, "");
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (repeatOptions.has(value)) {
|
|
60
|
+
const optionValue = args[index + 1];
|
|
61
|
+
if (optionValue === undefined || optionValue.startsWith("--")) {
|
|
62
|
+
throw usageError(`${value} is required.`, usage);
|
|
63
|
+
}
|
|
64
|
+
const existing = multiOptions.get(value) ?? [];
|
|
65
|
+
multiOptions.set(value, [...existing, optionValue]);
|
|
66
|
+
index += 1;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (options.has(value))
|
|
70
|
+
throw usageError(`Option may only be specified once: ${value}.`, usage);
|
|
71
|
+
const optionValue = args[index + 1];
|
|
72
|
+
if (optionValue === undefined || optionValue.startsWith("--")) {
|
|
73
|
+
throw usageError(`${value} is required.`, usage);
|
|
74
|
+
}
|
|
75
|
+
options.set(value, optionValue);
|
|
76
|
+
index += 1;
|
|
77
|
+
}
|
|
78
|
+
return { positionals, options, multiOptions };
|
|
79
|
+
}
|
|
80
|
+
export function parseTail(args, valueOptions, usage, flagOptions = new Set()) {
|
|
81
|
+
const positionals = [];
|
|
82
|
+
const options = new Map();
|
|
83
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
84
|
+
const value = args[index];
|
|
85
|
+
if (!value.startsWith("--")) {
|
|
86
|
+
positionals.push(value);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (!valueOptions.has(value) && !flagOptions.has(value)) {
|
|
90
|
+
throw usageError(`Unsupported option: ${value}.`, usage);
|
|
91
|
+
}
|
|
92
|
+
if (options.has(value))
|
|
93
|
+
throw usageError(`Option may only be specified once: ${value}.`, usage);
|
|
94
|
+
if (flagOptions.has(value)) {
|
|
95
|
+
options.set(value, "");
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const optionValue = args[index + 1];
|
|
99
|
+
if (optionValue === undefined || optionValue.startsWith("--")) {
|
|
100
|
+
throw usageError(`${value} is required.`, usage);
|
|
101
|
+
}
|
|
102
|
+
options.set(value, optionValue);
|
|
103
|
+
index += 1;
|
|
104
|
+
}
|
|
105
|
+
return { positionals, options };
|
|
106
|
+
}
|
|
107
|
+
export function requiredOption(options, name) {
|
|
108
|
+
return requiredText(options.get(name), name);
|
|
109
|
+
}
|
|
110
|
+
export function optionalNonEmptyOption(options, name) {
|
|
111
|
+
if (!options.has(name))
|
|
112
|
+
return undefined;
|
|
113
|
+
return requiredText(options.get(name), name);
|
|
114
|
+
}
|
|
115
|
+
export function exactPositionals(values, count, usage) {
|
|
116
|
+
if (values.length !== count || values.some((value) => value.trim().length === 0)) {
|
|
117
|
+
throw usageError(usage);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
export function requiredText(value, label) {
|
|
121
|
+
const normalized = value?.trim();
|
|
122
|
+
if (normalized === undefined || normalized.length === 0)
|
|
123
|
+
throw usageError(`${label} is required.`);
|
|
124
|
+
return normalized;
|
|
125
|
+
}
|
|
126
|
+
export function trimmed(value) {
|
|
127
|
+
const normalized = value?.trim();
|
|
128
|
+
return normalized === undefined || normalized.length === 0 ? undefined : normalized;
|
|
129
|
+
}
|
|
130
|
+
export function presentTime(value, timeZone) {
|
|
131
|
+
return formatTimestamp(value, timeZone);
|
|
132
|
+
}
|
|
133
|
+
export function output(value, data) {
|
|
134
|
+
return data === undefined
|
|
135
|
+
? { kind: "output", output: value }
|
|
136
|
+
: { kind: "output", output: value, data };
|
|
137
|
+
}
|
|
138
|
+
export function clock(options) {
|
|
139
|
+
return options.now?.() ?? new Date();
|
|
140
|
+
}
|
|
141
|
+
export function taskMailbox(taskId) {
|
|
142
|
+
return { kind: "task", taskId };
|
|
143
|
+
}
|
|
144
|
+
export function roleMailbox(taskId, roleName) {
|
|
145
|
+
return { kind: "role", taskId, roleName };
|
|
146
|
+
}
|
|
147
|
+
export function leaderMailbox(taskId) {
|
|
148
|
+
return roleMailbox(taskId, LEADER_ROLE);
|
|
149
|
+
}
|
|
150
|
+
export function taskRef(id) {
|
|
151
|
+
return { type: "task", id };
|
|
152
|
+
}
|
|
153
|
+
export function notifyMailbox(runtime, target) {
|
|
154
|
+
void runtime?.notifyMailboxChanged(target);
|
|
155
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|