agl 22.0.0 → 22.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +20 -0
- package/dist_serve/bundle.js +520 -509
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.authstore.d.ts +1 -2
- package/dist_ts/classes.authstore.js +1 -6
- package/dist_ts/classes.cli.d.ts +4 -2
- package/dist_ts/classes.cli.js +161 -68
- package/dist_ts/classes.controller.js +68 -30
- package/dist_ts/classes.upgradecoordinator.d.ts +39 -1
- package/dist_ts/classes.upgradecoordinator.js +740 -74
- package/dist_ts/classes.upgradetransaction.d.ts +68 -1
- package/dist_ts/classes.upgradetransaction.js +383 -223
- package/dist_ts_migration/classes.documentmigrationrunner.js +3 -1
- package/dist_ts_migration/index.d.ts +2 -0
- package/dist_ts_migration/index.js +3 -1
- package/dist_ts_migration/v24_legacyterminallayout.d.ts +18 -0
- package/dist_ts_migration/v24_legacyterminallayout.js +79 -0
- package/dist_ts_migration/v24_legacyterminallayoutmigration.d.ts +6 -0
- package/dist_ts_migration/v24_legacyterminallayoutmigration.js +67 -0
- package/package.json +1 -1
- package/readme.md +255 -378
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.authstore.ts +0 -15
- package/ts/classes.cli.ts +184 -83
- package/ts/classes.controller.ts +79 -33
- package/ts/classes.upgradecoordinator.ts +876 -74
- package/ts/classes.upgradetransaction.ts +574 -250
- package/ts_migration/classes.documentmigrationrunner.ts +2 -0
- package/ts_migration/index.ts +2 -0
- package/ts_migration/v24_legacyterminallayout.ts +131 -0
- package/ts_migration/v24_legacyterminallayoutmigration.ts +88 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements.harnesscontrollerapp.ts +32 -2
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/classes.authstore.ts
CHANGED
|
@@ -47,7 +47,6 @@ import type {
|
|
|
47
47
|
IControllerRuntimeId,
|
|
48
48
|
IControllerSessionGroup,
|
|
49
49
|
IControllerSessionLayout,
|
|
50
|
-
TControllerTerminalId,
|
|
51
50
|
TControllerResourceKind,
|
|
52
51
|
TControllerSessionId,
|
|
53
52
|
IControllerIntelligenceExchange,
|
|
@@ -1946,20 +1945,6 @@ export class SmartDataAuthStore implements IAuthStore {
|
|
|
1946
1945
|
);
|
|
1947
1946
|
}
|
|
1948
1947
|
|
|
1949
|
-
public async pruneTerminalLayoutEntries(
|
|
1950
|
-
projectIdArg: string,
|
|
1951
|
-
liveTerminalIdsArg: readonly TControllerTerminalId[],
|
|
1952
|
-
): Promise<boolean> {
|
|
1953
|
-
const liveTerminalKeys = new Set(liveTerminalIdsArg.map(controllerRuntimeIdKey));
|
|
1954
|
-
return this.filterSessionLayout(
|
|
1955
|
-
projectIdArg,
|
|
1956
|
-
(layoutItemId) => (
|
|
1957
|
-
layoutItemId.harnessId !== 'controller'
|
|
1958
|
-
|| liveTerminalKeys.has(controllerRuntimeIdKey(layoutItemId))
|
|
1959
|
-
),
|
|
1960
|
-
);
|
|
1961
|
-
}
|
|
1962
|
-
|
|
1963
1948
|
public async pruneArchivedSessionLayoutEntries(
|
|
1964
1949
|
projectIdArg: string,
|
|
1965
1950
|
archivedSessionIdsArg: readonly IControllerRuntimeId[],
|
package/ts/classes.cli.ts
CHANGED
|
@@ -39,20 +39,30 @@ import {
|
|
|
39
39
|
} from './classes.processinspection.js';
|
|
40
40
|
import {
|
|
41
41
|
createUpgradeToken,
|
|
42
|
+
normalizeUpgradeRegistryUrl,
|
|
42
43
|
parseUpgradeWorkerPayload,
|
|
43
44
|
UpgradeCoordinator,
|
|
44
45
|
upgradePackageTransitionSource,
|
|
45
46
|
upgradePackageTransitionTarget,
|
|
47
|
+
upgradeTransactionTargetVersion,
|
|
46
48
|
type UpgradeStartLease,
|
|
47
49
|
} from './classes.upgradecoordinator.js';
|
|
48
50
|
import { upgradeTokenEnvironmentVariable } from './constants.upgradeenvironment.js';
|
|
49
51
|
import {
|
|
52
|
+
adoptOrphanedUpgradeUnderLock,
|
|
50
53
|
createUpgradeWorkerPayload,
|
|
54
|
+
handoffLaunchedUpgradeWorkerUnderLock,
|
|
55
|
+
handoffStalledUpgradeRecoveryUnderLock,
|
|
56
|
+
type IUpgradeWorkerLaunchCandidate,
|
|
57
|
+
inspectOrphanedUpgradeForAdoption,
|
|
51
58
|
launchDetachedUpgradeWorker,
|
|
52
59
|
resolveCurrentCliPath,
|
|
53
60
|
resolveCurrentPnpmGlobalInstallation,
|
|
54
61
|
runUpgradeWorker,
|
|
62
|
+
terminalizeUpgradeWorkerHandoffFailure,
|
|
55
63
|
tryResolveCurrentPnpmGlobalInstallation,
|
|
64
|
+
UpgradeWorkerCandidateDrainageError,
|
|
65
|
+
UpgradeWorkerHandoffError,
|
|
56
66
|
} from './classes.upgradetransaction.js';
|
|
57
67
|
import { followUpgradeTransaction } from './functions.upgradefollower.js';
|
|
58
68
|
|
|
@@ -64,6 +74,10 @@ const currentCliName = commitinfo.name === upgradePackageTransitionSource.packag
|
|
|
64
74
|
const currentRuntimeName = currentCliName === upgradePackageTransitionTarget.cliName
|
|
65
75
|
? 'AGL'
|
|
66
76
|
: currentCliName;
|
|
77
|
+
const upgradeRegistryUsage = currentRuntimeName === 'AGL' ? ' [--registry <url>]' : '';
|
|
78
|
+
const upgradeRegistryHelp = currentRuntimeName === 'AGL'
|
|
79
|
+
? ' --registry <url> Override pnpm\'s configured registry for this upgrade\n'
|
|
80
|
+
: '';
|
|
67
81
|
|
|
68
82
|
const helpText = `
|
|
69
83
|
${currentRuntimeName === 'AGL' ? 'AGL - Agent Gateway Layer' : currentRuntimeName}
|
|
@@ -72,7 +86,7 @@ Usage:
|
|
|
72
86
|
${currentCliName} start [options]
|
|
73
87
|
${currentCliName} status [--port 4097] [--json]
|
|
74
88
|
${currentCliName} stop [--port 4097]
|
|
75
|
-
${currentCliName} upgrade [--port 4097] [--grace-period-seconds 300] [--continue-sessions] [--json]
|
|
89
|
+
${currentCliName} upgrade [--port 4097]${upgradeRegistryUsage} [--grace-period-seconds 300] [--continue-sessions] [--json]
|
|
76
90
|
${currentCliName} foreground [options]
|
|
77
91
|
${currentCliName} temp-password [--port 4097] [--ttl-hours 24]
|
|
78
92
|
|
|
@@ -100,7 +114,7 @@ Startup options:
|
|
|
100
114
|
characters; short codes weaken brute-force resistance)
|
|
101
115
|
|
|
102
116
|
Upgrade options:
|
|
103
|
-
--grace-period-seconds <n> Maximum graceful pause time (default: 300)
|
|
117
|
+
${upgradeRegistryHelp} --grace-period-seconds <n> Maximum graceful pause time (default: 300)
|
|
104
118
|
--continue-sessions Continue sessions paused for the upgrade
|
|
105
119
|
|
|
106
120
|
Temp password options:
|
|
@@ -956,46 +970,70 @@ const stopController = async (argvArg: TParsedArguments): Promise<void> => {
|
|
|
956
970
|
process.stdout.write(`${currentRuntimeName} stopped.\n`);
|
|
957
971
|
};
|
|
958
972
|
|
|
973
|
+
export interface ICreateCurrentPackageUpgradeTransactionOptions {
|
|
974
|
+
token: string;
|
|
975
|
+
port: number;
|
|
976
|
+
sourceVersion: string;
|
|
977
|
+
registryUrl?: string;
|
|
978
|
+
controllerWasRunning: boolean;
|
|
979
|
+
continueSessions: boolean;
|
|
980
|
+
gracePeriodMs: number;
|
|
981
|
+
}
|
|
982
|
+
|
|
959
983
|
export const createCurrentPackageUpgradeTransaction = async (
|
|
960
984
|
coordinatorArg: UpgradeCoordinator,
|
|
961
|
-
optionsArg:
|
|
962
|
-
token: string;
|
|
963
|
-
port: number;
|
|
964
|
-
sourceVersion: string;
|
|
965
|
-
controllerWasRunning: boolean;
|
|
966
|
-
continueSessions: boolean;
|
|
967
|
-
gracePeriodMs: number;
|
|
968
|
-
},
|
|
985
|
+
optionsArg: ICreateCurrentPackageUpgradeTransactionOptions,
|
|
969
986
|
) => {
|
|
970
987
|
if (
|
|
971
988
|
commitinfo.name === upgradePackageTransitionSource.packageName
|
|
972
989
|
&& optionsArg.sourceVersion === upgradePackageTransitionSource.version
|
|
973
990
|
) {
|
|
991
|
+
if (optionsArg.registryUrl !== undefined) {
|
|
992
|
+
throw new Error(
|
|
993
|
+
'The legacy hcon package transition uses pnpm registry configuration and cannot retain --registry.',
|
|
994
|
+
);
|
|
995
|
+
}
|
|
974
996
|
return await coordinatorArg.createPackageTransitionTransaction(optionsArg);
|
|
975
997
|
}
|
|
976
998
|
return await coordinatorArg.createTransaction(optionsArg);
|
|
977
999
|
};
|
|
978
1000
|
|
|
979
1001
|
const upgradeController = async (argvArg: TParsedArguments): Promise<void> => {
|
|
980
|
-
await preflightAGLHomeMigration();
|
|
981
1002
|
if (process.platform !== 'linux' && process.platform !== 'darwin') {
|
|
982
1003
|
throw new Error(`${currentCliName} upgrade is currently supported on Linux and macOS only.`);
|
|
983
1004
|
}
|
|
984
1005
|
const port = readPortOption(argvArg, 'port', 'port', defaultControllerPort)!;
|
|
1006
|
+
const registryValue = readStringOption(argvArg, 'registry', 'registry');
|
|
1007
|
+
const registryUrl = registryValue === undefined
|
|
1008
|
+
? undefined
|
|
1009
|
+
: normalizeUpgradeRegistryUrl(registryValue);
|
|
985
1010
|
const gracePeriodMs = readUpgradeGracePeriodMs(argvArg);
|
|
986
1011
|
const continueSessions = readBooleanOption(argvArg, 'continueSessions', 'continue-sessions');
|
|
987
1012
|
const installation = await resolveCurrentPnpmGlobalInstallation();
|
|
1013
|
+
const coordinator = new UpgradeCoordinator(installation.globalRoot);
|
|
1014
|
+
await coordinator.initializeExistingCanonicalState();
|
|
1015
|
+
const orphaned = await inspectOrphanedUpgradeForAdoption({
|
|
1016
|
+
coordinator,
|
|
1017
|
+
installation,
|
|
1018
|
+
port,
|
|
1019
|
+
...(registryUrl === undefined ? {} : { registryUrl }),
|
|
1020
|
+
});
|
|
1021
|
+
if (!orphaned) await preflightAGLHomeMigration();
|
|
988
1022
|
const status = await tryQueryControllerStatus(port);
|
|
989
1023
|
if (!status && await isLoopbackPortListening(port)) {
|
|
990
1024
|
throw new Error(
|
|
991
1025
|
`Port ${port} is occupied by an incompatible service or controller version; refusing to upgrade.`,
|
|
992
1026
|
);
|
|
993
1027
|
}
|
|
994
|
-
if (
|
|
1028
|
+
if (orphaned && status) {
|
|
1029
|
+
throw new Error('An orphaned upgrade cannot be adopted while a controller is running.');
|
|
1030
|
+
}
|
|
1031
|
+
if (!status && !orphaned) await ensureControllerDataRoot();
|
|
1032
|
+
const retainedTransaction = orphaned?.transaction;
|
|
995
1033
|
const payload = createUpgradeWorkerPayload({
|
|
996
1034
|
port,
|
|
997
|
-
gracePeriodMs,
|
|
998
|
-
continueSessions,
|
|
1035
|
+
gracePeriodMs: retainedTransaction?.gracePeriodMs ?? gracePeriodMs,
|
|
1036
|
+
continueSessions: retainedTransaction?.continueSessions ?? continueSessions,
|
|
999
1037
|
...(status
|
|
1000
1038
|
? {
|
|
1001
1039
|
expectedController: {
|
|
@@ -1006,88 +1044,147 @@ const upgradeController = async (argvArg: TParsedArguments): Promise<void> => {
|
|
|
1006
1044
|
}
|
|
1007
1045
|
: {}),
|
|
1008
1046
|
});
|
|
1009
|
-
const coordinator = new UpgradeCoordinator(installation.globalRoot);
|
|
1010
1047
|
const invocationToken = createUpgradeToken();
|
|
1011
1048
|
const invocationLease = await coordinator.acquireUpgradeLock({
|
|
1012
1049
|
token: invocationToken,
|
|
1013
1050
|
cliPath: installation.cliPath,
|
|
1014
1051
|
command: 'upgrade',
|
|
1015
1052
|
});
|
|
1016
|
-
let
|
|
1053
|
+
let invocationLeaseHandled = false;
|
|
1017
1054
|
let invocationError: unknown;
|
|
1018
1055
|
try {
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1056
|
+
let worker: IUpgradeWorkerLaunchCandidate;
|
|
1057
|
+
let controllerWasRunning = status !== undefined;
|
|
1058
|
+
if (orphaned) {
|
|
1059
|
+
const adopted = await adoptOrphanedUpgradeUnderLock({
|
|
1060
|
+
coordinator,
|
|
1061
|
+
lock: invocationLease,
|
|
1062
|
+
token: payload.token,
|
|
1063
|
+
expected: orphaned,
|
|
1064
|
+
installation,
|
|
1065
|
+
port,
|
|
1066
|
+
...(registryUrl === undefined ? {} : { registryUrl }),
|
|
1067
|
+
});
|
|
1068
|
+
controllerWasRunning = adopted.controllerWasRunning;
|
|
1069
|
+
let launched: Awaited<ReturnType<typeof handoffStalledUpgradeRecoveryUnderLock>>;
|
|
1070
|
+
try {
|
|
1071
|
+
launched = await handoffStalledUpgradeRecoveryUnderLock({
|
|
1072
|
+
coordinator,
|
|
1031
1073
|
token: payload.token,
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
cliPath: installation.cliPath,
|
|
1036
|
-
controller: payload.expectedController!,
|
|
1074
|
+
installation,
|
|
1075
|
+
command: 'upgrade',
|
|
1076
|
+
lock: invocationLease,
|
|
1037
1077
|
});
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1078
|
+
} finally {
|
|
1079
|
+
invocationLeaseHandled = true;
|
|
1080
|
+
}
|
|
1081
|
+
if (!launched) throw new Error('The adopted upgrade became terminal before recovery launch.');
|
|
1082
|
+
worker = launched;
|
|
1083
|
+
} else {
|
|
1084
|
+
const racedOrphan = await inspectOrphanedUpgradeForAdoption({
|
|
1085
|
+
coordinator,
|
|
1086
|
+
installation,
|
|
1087
|
+
port,
|
|
1088
|
+
...(registryUrl === undefined ? {} : { registryUrl }),
|
|
1089
|
+
});
|
|
1090
|
+
if (racedOrphan) {
|
|
1091
|
+
throw new Error('An orphaned upgrade appeared after normal upgrade preflight.');
|
|
1092
|
+
}
|
|
1093
|
+
await createCurrentPackageUpgradeTransaction(coordinator, {
|
|
1094
|
+
token: payload.token,
|
|
1095
|
+
port,
|
|
1096
|
+
sourceVersion: installation.packageVersion,
|
|
1097
|
+
...(registryUrl === undefined ? {} : { registryUrl }),
|
|
1098
|
+
controllerWasRunning,
|
|
1099
|
+
continueSessions,
|
|
1100
|
+
gracePeriodMs: payload.gracePeriodMs,
|
|
1101
|
+
});
|
|
1102
|
+
try {
|
|
1103
|
+
if (status) {
|
|
1104
|
+
await coordinator.createLaunchGrant({
|
|
1105
|
+
token: payload.token,
|
|
1106
|
+
port,
|
|
1107
|
+
packageName: controllerPackageName,
|
|
1108
|
+
packageVersion: commitinfo.version,
|
|
1109
|
+
cliPath: installation.cliPath,
|
|
1110
|
+
controller: payload.expectedController!,
|
|
1111
|
+
});
|
|
1112
|
+
try {
|
|
1113
|
+
const launched = await requestControllerUpgradeLaunch(port, payload.token);
|
|
1114
|
+
worker = {
|
|
1115
|
+
pid: launched.workerPid,
|
|
1116
|
+
cliPath: installation.cliPath,
|
|
1117
|
+
logFilePath: launched.logFilePath,
|
|
1118
|
+
};
|
|
1119
|
+
} finally {
|
|
1120
|
+
await coordinator.removeLaunchGrant(payload.token);
|
|
1121
|
+
}
|
|
1122
|
+
} else {
|
|
1123
|
+
const launched = await launchDetachedUpgradeWorker({ installation, payload });
|
|
1124
|
+
worker = launched;
|
|
1125
|
+
}
|
|
1126
|
+
} catch (errorArg) {
|
|
1127
|
+
if (errorArg instanceof UpgradeWorkerCandidateDrainageError || status) {
|
|
1128
|
+
invocationLeaseHandled = true;
|
|
1129
|
+
throw errorArg instanceof UpgradeWorkerCandidateDrainageError
|
|
1130
|
+
? errorArg
|
|
1131
|
+
: new UpgradeWorkerCandidateDrainageError(
|
|
1132
|
+
'The controller upgrade launch failed and candidate drainage cannot be proven by the CLI.',
|
|
1133
|
+
errorArg,
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
const transaction = await coordinator.readTransaction(payload.token);
|
|
1137
|
+
if (transaction.worker?.logFilePath) {
|
|
1138
|
+
worker = {
|
|
1139
|
+
pid: transaction.worker.pid,
|
|
1140
|
+
processGroupId: transaction.worker.processGroupId,
|
|
1141
|
+
fingerprint: transaction.worker.fingerprint,
|
|
1142
|
+
cliPath: transaction.worker.cliPath,
|
|
1143
|
+
logFilePath: transaction.worker.logFilePath,
|
|
1144
|
+
};
|
|
1145
|
+
} else {
|
|
1146
|
+
await coordinator.finishTransaction(
|
|
1147
|
+
payload.token,
|
|
1148
|
+
false,
|
|
1149
|
+
'The upgrade worker could not be launched.',
|
|
1150
|
+
errorArg instanceof Error ? errorArg.message : String(errorArg),
|
|
1151
|
+
);
|
|
1152
|
+
throw errorArg;
|
|
1042
1153
|
}
|
|
1043
|
-
} else {
|
|
1044
|
-
const launched = await launchDetachedUpgradeWorker({ installation, payload });
|
|
1045
|
-
worker = { workerPid: launched.pid, logFilePath: launched.logFilePath };
|
|
1046
1154
|
}
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
errorArg instanceof
|
|
1060
|
-
|
|
1061
|
-
|
|
1155
|
+
try {
|
|
1156
|
+
try {
|
|
1157
|
+
worker = await handoffLaunchedUpgradeWorkerUnderLock({
|
|
1158
|
+
coordinator,
|
|
1159
|
+
token: payload.token,
|
|
1160
|
+
installation,
|
|
1161
|
+
command: 'upgrade',
|
|
1162
|
+
lock: invocationLease,
|
|
1163
|
+
candidate: worker,
|
|
1164
|
+
retainLockOnFailure: true,
|
|
1165
|
+
});
|
|
1166
|
+
} catch (errorArg) {
|
|
1167
|
+
if (!(errorArg instanceof UpgradeWorkerHandoffError)) throw errorArg;
|
|
1168
|
+
await terminalizeUpgradeWorkerHandoffFailure({
|
|
1169
|
+
coordinator,
|
|
1170
|
+
token: payload.token,
|
|
1171
|
+
error: errorArg,
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
} finally {
|
|
1175
|
+
invocationLeaseHandled = true;
|
|
1062
1176
|
}
|
|
1063
1177
|
}
|
|
1064
|
-
await coordinator.recordWorkerLaunch(
|
|
1065
|
-
payload.token,
|
|
1066
|
-
worker.workerPid,
|
|
1067
|
-
installation.cliPath,
|
|
1068
|
-
worker.logFilePath,
|
|
1069
|
-
);
|
|
1070
1178
|
const json = readBooleanOption(argvArg, 'json');
|
|
1071
1179
|
if (!json) {
|
|
1072
1180
|
process.stdout.write([
|
|
1073
|
-
`Upgrade worker started (PID ${worker.
|
|
1181
|
+
`Upgrade worker started (PID ${worker.pid}).`,
|
|
1074
1182
|
`Logs: ${worker.logFilePath}`,
|
|
1075
|
-
|
|
1183
|
+
controllerWasRunning
|
|
1076
1184
|
? `Controller on port ${port} will restart after a successful upgrade.`
|
|
1077
1185
|
: 'No running controller was found; it will remain stopped.',
|
|
1078
1186
|
].join('\n') + '\n');
|
|
1079
1187
|
}
|
|
1080
|
-
const launchedTransaction = await coordinator.readTransaction(payload.token);
|
|
1081
|
-
if (!launchedTransaction.worker) {
|
|
1082
|
-
throw new Error('The launched upgrade worker identity was not retained.');
|
|
1083
|
-
}
|
|
1084
|
-
await coordinator.transferUpgradeLockToWorker({
|
|
1085
|
-
lock: invocationLease,
|
|
1086
|
-
token: payload.token,
|
|
1087
|
-
worker: launchedTransaction.worker,
|
|
1088
|
-
});
|
|
1089
|
-
handoffStarted = true;
|
|
1090
|
-
await coordinator.acknowledgeWorker(payload.token);
|
|
1091
1188
|
let finalTransaction: Awaited<ReturnType<UpgradeCoordinator['readTransaction']>>;
|
|
1092
1189
|
let followError: unknown;
|
|
1093
1190
|
try {
|
|
@@ -1105,16 +1202,17 @@ const upgradeController = async (argvArg: TParsedArguments): Promise<void> => {
|
|
|
1105
1202
|
tokenHash: '',
|
|
1106
1203
|
revision: 0,
|
|
1107
1204
|
port,
|
|
1108
|
-
sourceVersion: installation.packageVersion,
|
|
1109
|
-
|
|
1110
|
-
|
|
1205
|
+
sourceVersion: retainedTransaction?.sourceVersion ?? installation.packageVersion,
|
|
1206
|
+
...(registryUrl === undefined ? {} : { registryUrl }),
|
|
1207
|
+
controllerWasRunning,
|
|
1208
|
+
continueSessions: payload.continueSessions,
|
|
1111
1209
|
gracePeriodMs: payload.gracePeriodMs,
|
|
1112
1210
|
phase: 'failed' as const,
|
|
1113
1211
|
message: errorArg instanceof Error ? errorArg.message : String(errorArg),
|
|
1114
1212
|
createdAt: Date.now(),
|
|
1115
1213
|
phaseStartedAt: Date.now(),
|
|
1116
1214
|
updatedAt: Date.now(),
|
|
1117
|
-
sessions: [],
|
|
1215
|
+
sessions: retainedTransaction?.sessions ?? [],
|
|
1118
1216
|
terminal: {
|
|
1119
1217
|
success: false,
|
|
1120
1218
|
error: errorArg instanceof Error ? errorArg.message : String(errorArg),
|
|
@@ -1127,7 +1225,7 @@ const upgradeController = async (argvArg: TParsedArguments): Promise<void> => {
|
|
|
1127
1225
|
workerPid: finalTransaction.worker.pid,
|
|
1128
1226
|
logFilePath: finalTransaction.worker.logFilePath,
|
|
1129
1227
|
}
|
|
1130
|
-
: worker;
|
|
1228
|
+
: { workerPid: worker.pid, logFilePath: worker.logFilePath };
|
|
1131
1229
|
const sessionSummary = finalTransaction.sessions.reduce((summary, session) => {
|
|
1132
1230
|
summary.pause[session.pauseState] = (summary.pause[session.pauseState] ?? 0) + 1;
|
|
1133
1231
|
if (session.continueState) {
|
|
@@ -1142,9 +1240,9 @@ const upgradeController = async (argvArg: TParsedArguments): Promise<void> => {
|
|
|
1142
1240
|
process.stdout.write(JSON.stringify({
|
|
1143
1241
|
accepted: true,
|
|
1144
1242
|
...reportedWorker,
|
|
1145
|
-
controllerWasRunning
|
|
1243
|
+
controllerWasRunning,
|
|
1146
1244
|
fromVersion: finalTransaction.sourceVersion,
|
|
1147
|
-
toVersion: finalTransaction
|
|
1245
|
+
toVersion: upgradeTransactionTargetVersion(finalTransaction),
|
|
1148
1246
|
phase: finalTransaction.phase,
|
|
1149
1247
|
success: finalTransaction.terminal?.success === true && followError === undefined,
|
|
1150
1248
|
error: finalTransaction.terminal?.error
|
|
@@ -1156,13 +1254,16 @@ const upgradeController = async (argvArg: TParsedArguments): Promise<void> => {
|
|
|
1156
1254
|
}
|
|
1157
1255
|
if (followError) throw followError;
|
|
1158
1256
|
if (!finalTransaction.terminal?.success) {
|
|
1159
|
-
|
|
1257
|
+
const terminalError = new Error(
|
|
1258
|
+
finalTransaction.terminal?.error ?? `${currentCliName} upgrade failed.`,
|
|
1259
|
+
);
|
|
1260
|
+
throw terminalError;
|
|
1160
1261
|
}
|
|
1161
1262
|
} catch (errorArg) {
|
|
1162
1263
|
invocationError = errorArg;
|
|
1163
1264
|
}
|
|
1164
1265
|
let releaseError: unknown;
|
|
1165
|
-
if (!
|
|
1266
|
+
if (!invocationLeaseHandled) {
|
|
1166
1267
|
try {
|
|
1167
1268
|
await invocationLease.release();
|
|
1168
1269
|
} catch (errorArg) {
|
package/ts/classes.controller.ts
CHANGED
|
@@ -291,6 +291,7 @@ import {
|
|
|
291
291
|
upgradePackageTransitionTarget,
|
|
292
292
|
upgradePackageTransitionTransactionVersion,
|
|
293
293
|
upgradeTransactionIsStalled,
|
|
294
|
+
upgradeTransactionTargetVersion,
|
|
294
295
|
type UpgradeInstallationLock,
|
|
295
296
|
type IUpgradeSessionState,
|
|
296
297
|
type TUpgradeTransaction,
|
|
@@ -301,6 +302,8 @@ import {
|
|
|
301
302
|
launchDetachedUpgradeWorker,
|
|
302
303
|
resolveCurrentPnpmGlobalInstallation,
|
|
303
304
|
resolveInheritedUpgradeTargetInstallation,
|
|
305
|
+
terminateUpgradeWorkerCandidateUntilDrained,
|
|
306
|
+
type IDetachedUpgradeWorker,
|
|
304
307
|
} from './classes.upgradetransaction.js';
|
|
305
308
|
import {
|
|
306
309
|
upgradeCoordinationRootEnvironmentVariable,
|
|
@@ -1378,6 +1381,13 @@ interface IControllerHarnessSessionEnumeration {
|
|
|
1378
1381
|
flex: PromiseSettledResult<IControllerSession[]>;
|
|
1379
1382
|
}
|
|
1380
1383
|
|
|
1384
|
+
interface IControllerHarnessSessionEnumerationTask {
|
|
1385
|
+
task: Promise<IControllerHarnessSessionEnumeration>;
|
|
1386
|
+
invalidated: boolean;
|
|
1387
|
+
settled: boolean;
|
|
1388
|
+
waiterCount: number;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1381
1391
|
export class OpenCodeController {
|
|
1382
1392
|
public readonly options: IControllerStartOptions;
|
|
1383
1393
|
|
|
@@ -1509,7 +1519,7 @@ export class OpenCodeController {
|
|
|
1509
1519
|
private readonly activeOperations = new Set<Promise<unknown>>();
|
|
1510
1520
|
private readonly harnessSessionEnumerationTasks = new Map<
|
|
1511
1521
|
string,
|
|
1512
|
-
|
|
1522
|
+
IControllerHarnessSessionEnumerationTask
|
|
1513
1523
|
>();
|
|
1514
1524
|
private readonly sessionLayoutOperationTails = new Map<string, Promise<void>>();
|
|
1515
1525
|
private readonly sessionLayoutOperationDepths = new Map<string, number>();
|
|
@@ -2015,12 +2025,6 @@ export class OpenCodeController {
|
|
|
2015
2025
|
);
|
|
2016
2026
|
const startupProjectState = await this.reconcileProjectState(false);
|
|
2017
2027
|
for (const project of pendingProjectRemovals) this.retiringProjectIds.add(project.id);
|
|
2018
|
-
for (const project of startupProjectState.projects) {
|
|
2019
|
-
await this.runSessionLayoutOperation(
|
|
2020
|
-
project.id,
|
|
2021
|
-
() => authStore.pruneTerminalLayoutEntries(project.id, []),
|
|
2022
|
-
);
|
|
2023
|
-
}
|
|
2024
2028
|
const startupSettings = await authStore.getSettings();
|
|
2025
2029
|
this.defaultModels = startupSettings.defaultModels;
|
|
2026
2030
|
this.autoAcceptPermissions = startupSettings.autoAcceptPermissions === true;
|
|
@@ -3367,10 +3371,12 @@ export class OpenCodeController {
|
|
|
3367
3371
|
);
|
|
3368
3372
|
}
|
|
3369
3373
|
return this.runOperation(peer, async (signalArg) => {
|
|
3374
|
+
let coordinator: UpgradeCoordinator | undefined;
|
|
3375
|
+
let worker: IDetachedUpgradeWorker | undefined;
|
|
3370
3376
|
try {
|
|
3371
3377
|
const installation = await resolveCurrentPnpmGlobalInstallation();
|
|
3372
3378
|
const status = this.getStatus();
|
|
3373
|
-
|
|
3379
|
+
coordinator = this.coordinatorForUpgradeToken(
|
|
3374
3380
|
requestArg.token,
|
|
3375
3381
|
installation.globalRoot,
|
|
3376
3382
|
);
|
|
@@ -3387,7 +3393,7 @@ export class OpenCodeController {
|
|
|
3387
3393
|
},
|
|
3388
3394
|
});
|
|
3389
3395
|
const transaction = await coordinator.readTransaction(requestArg.token);
|
|
3390
|
-
|
|
3396
|
+
worker = await launchDetachedUpgradeWorker({
|
|
3391
3397
|
installation,
|
|
3392
3398
|
payload: {
|
|
3393
3399
|
version: upgradeCoordinationVersion,
|
|
@@ -3414,7 +3420,18 @@ export class OpenCodeController {
|
|
|
3414
3420
|
logFilePath: worker.logFilePath,
|
|
3415
3421
|
};
|
|
3416
3422
|
} catch (errorArg) {
|
|
3417
|
-
|
|
3423
|
+
let reportedError: unknown = errorArg;
|
|
3424
|
+
if (worker && coordinator) {
|
|
3425
|
+
try {
|
|
3426
|
+
await terminateUpgradeWorkerCandidateUntilDrained(coordinator, worker);
|
|
3427
|
+
} catch (cleanupErrorArg) {
|
|
3428
|
+
reportedError = new AggregateError(
|
|
3429
|
+
[errorArg, cleanupErrorArg],
|
|
3430
|
+
`${currentCliName} upgrade worker launch failed and its candidate did not drain.`,
|
|
3431
|
+
);
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
console.error(`${currentCliName} upgrade worker launch failed:`, reportedError);
|
|
3418
3435
|
throw new plugins.typedrequest.TypedResponseError(
|
|
3419
3436
|
`The ${currentCliName} upgrade launch was not authorized or could not be started.`,
|
|
3420
3437
|
{ code: 'upgrade_launch_failed' },
|
|
@@ -7360,31 +7377,55 @@ export class OpenCodeController {
|
|
|
7360
7377
|
}
|
|
7361
7378
|
}
|
|
7362
7379
|
|
|
7363
|
-
private enumerateHarnessSessions(
|
|
7380
|
+
private async enumerateHarnessSessions(
|
|
7364
7381
|
projectIdArg: string,
|
|
7365
7382
|
directoryArg: string,
|
|
7366
7383
|
signalArg: AbortSignal,
|
|
7367
7384
|
): Promise<IControllerHarnessSessionEnumeration> {
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
Promise.
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
|
|
7385
|
+
while (true) {
|
|
7386
|
+
signalArg.throwIfAborted();
|
|
7387
|
+
let shared = this.harnessSessionEnumerationTasks.get(projectIdArg);
|
|
7388
|
+
if (!shared || shared.invalidated) {
|
|
7389
|
+
const lifecycleSignal = this.operationAbortController.signal;
|
|
7390
|
+
const operation = Promise.allSettled([
|
|
7391
|
+
Promise.resolve().then(() => (
|
|
7392
|
+
this.requireOpenCodeClient().listSessions(directoryArg, lifecycleSignal)
|
|
7393
|
+
)),
|
|
7394
|
+
Promise.resolve().then(() => this.listFlexSessions(projectIdArg, lifecycleSignal)),
|
|
7395
|
+
]).then(([openCode, flex]) => ({ openCode, flex }));
|
|
7396
|
+
let taskRecord!: IControllerHarnessSessionEnumerationTask;
|
|
7397
|
+
let trackedTask!: Promise<IControllerHarnessSessionEnumeration>;
|
|
7398
|
+
trackedTask = this.trackOperationPromise(operation, () => {
|
|
7399
|
+
taskRecord.settled = true;
|
|
7400
|
+
const current = this.harnessSessionEnumerationTasks.get(projectIdArg);
|
|
7401
|
+
if (current === taskRecord && taskRecord.waiterCount === 0) {
|
|
7402
|
+
this.harnessSessionEnumerationTasks.delete(projectIdArg);
|
|
7403
|
+
}
|
|
7404
|
+
});
|
|
7405
|
+
taskRecord = {
|
|
7406
|
+
task: trackedTask,
|
|
7407
|
+
invalidated: false,
|
|
7408
|
+
settled: false,
|
|
7409
|
+
waiterCount: 0,
|
|
7410
|
+
};
|
|
7411
|
+
shared = taskRecord;
|
|
7412
|
+
this.harnessSessionEnumerationTasks.set(projectIdArg, shared);
|
|
7413
|
+
}
|
|
7414
|
+
shared.waiterCount += 1;
|
|
7415
|
+
try {
|
|
7416
|
+
const result = await this.waitForPromiseWithSignal(shared.task, signalArg);
|
|
7417
|
+
if (!shared.invalidated) return result;
|
|
7418
|
+
} finally {
|
|
7419
|
+
shared.waiterCount -= 1;
|
|
7420
|
+
if (
|
|
7421
|
+
shared.settled
|
|
7422
|
+
&& shared.waiterCount === 0
|
|
7423
|
+
&& this.harnessSessionEnumerationTasks.get(projectIdArg) === shared
|
|
7424
|
+
) {
|
|
7381
7425
|
this.harnessSessionEnumerationTasks.delete(projectIdArg);
|
|
7382
7426
|
}
|
|
7383
|
-
}
|
|
7384
|
-
this.harnessSessionEnumerationTasks.set(projectIdArg, trackedTask);
|
|
7385
|
-
sharedTask = trackedTask;
|
|
7427
|
+
}
|
|
7386
7428
|
}
|
|
7387
|
-
return this.waitForPromiseWithSignal(sharedTask, signalArg);
|
|
7388
7429
|
}
|
|
7389
7430
|
|
|
7390
7431
|
private toFlexSession(
|
|
@@ -11720,7 +11761,7 @@ export class OpenCodeController {
|
|
|
11720
11761
|
signalArg?: AbortSignal,
|
|
11721
11762
|
): Promise<IControllerSession> {
|
|
11722
11763
|
if (sessionIdArg.harnessId === 'opencode') {
|
|
11723
|
-
return this.requireOpenCodeClient().
|
|
11764
|
+
return this.requireOpenCodeClient().getSessionSnapshot(
|
|
11724
11765
|
directoryArg,
|
|
11725
11766
|
sessionIdArg.nativeId,
|
|
11726
11767
|
signalArg,
|
|
@@ -11977,6 +12018,9 @@ export class OpenCodeController {
|
|
|
11977
12018
|
projectIdArg: string,
|
|
11978
12019
|
sessionIdArg: IControllerRuntimeId,
|
|
11979
12020
|
): void {
|
|
12021
|
+
const enumeration = this.harnessSessionEnumerationTasks.get(projectIdArg);
|
|
12022
|
+
if (enumeration) enumeration.invalidated = true;
|
|
12023
|
+
this.harnessSessionEnumerationTasks.delete(projectIdArg);
|
|
11980
12024
|
if (sessionIdArg.harnessId === 'opencode') {
|
|
11981
12025
|
this.purgeOpenCodeStreamStateForSession(projectIdArg, sessionIdArg);
|
|
11982
12026
|
} else if (sessionIdArg.harnessId === 'flex') {
|
|
@@ -13703,9 +13747,10 @@ export class OpenCodeController {
|
|
|
13703
13747
|
|| session.continueState === 'pending'
|
|
13704
13748
|
|| session.continueState === 'failed')
|
|
13705
13749
|
));
|
|
13706
|
-
|
|
13750
|
+
const targetVersion = upgradeTransactionTargetVersion(transaction);
|
|
13751
|
+
this.upgradeStatus = targetVersion ? {
|
|
13707
13752
|
fromVersion: transaction.sourceVersion,
|
|
13708
|
-
toVersion:
|
|
13753
|
+
toVersion: targetVersion,
|
|
13709
13754
|
phase: 'continuing',
|
|
13710
13755
|
} : this.upgradeStatus;
|
|
13711
13756
|
await coordinatorArg.mutateTransaction(tokenArg, (current) => ({
|
|
@@ -13750,9 +13795,10 @@ export class OpenCodeController {
|
|
|
13750
13795
|
this.harnessOperationManager.endUpgradeBarrier(this.upgradeBarrier);
|
|
13751
13796
|
this.upgradeBarrier = undefined;
|
|
13752
13797
|
}
|
|
13753
|
-
|
|
13798
|
+
const targetVersion = upgradeTransactionTargetVersion(transaction);
|
|
13799
|
+
this.upgradeStatus = targetVersion ? {
|
|
13754
13800
|
fromVersion: transaction.sourceVersion,
|
|
13755
|
-
toVersion:
|
|
13801
|
+
toVersion: targetVersion,
|
|
13756
13802
|
phase: failed > 0 || modeArg === 'compensate' ? 'failed' : 'completed',
|
|
13757
13803
|
} : this.upgradeStatus;
|
|
13758
13804
|
if (this.upgradeStatus) {
|