agl 21.0.2 → 22.0.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/changelog.md +9 -0
- package/dist_serve/bundle.js +1 -1
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.aglhome.d.ts +25 -0
- package/dist_ts/classes.aglhome.js +65 -0
- package/dist_ts/classes.authmodels.js +7 -12
- package/dist_ts/classes.authstore.js +2 -29
- package/dist_ts/classes.cli.js +93 -35
- package/dist_ts/classes.config.d.ts +2 -11
- package/dist_ts/classes.config.js +14 -80
- package/dist_ts/classes.controller.d.ts +4 -1
- package/dist_ts/classes.controller.js +64 -26
- package/dist_ts/classes.embeddeddb.js +6 -5
- package/dist_ts/classes.gitreversion.js +3 -2
- package/dist_ts/classes.upgradecoordinator.d.ts +6 -1
- package/dist_ts/classes.upgradecoordinator.js +588 -177
- package/dist_ts/classes.upgradetransaction.js +46 -12
- package/dist_ts/classes.uploadmanager.d.ts +12 -0
- package/dist_ts/classes.uploadmanager.js +204 -2
- package/dist_ts/constants.upgradeenvironment.d.ts +2 -0
- package/dist_ts/constants.upgradeenvironment.js +3 -0
- package/dist_ts/functions.controllerdataroot.d.ts +4 -5
- package/dist_ts/functions.controllerdataroot.js +4 -29
- package/dist_ts/functions.embeddeddb.d.ts +1 -1
- package/dist_ts/functions.embeddeddb.js +8 -3
- package/dist_ts/functions.runtimeenvironment.js +2 -1
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +2 -1
- package/dist_ts/interfaces.config.d.ts +5 -7
- 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/v23_aglhome.d.ts +91 -0
- package/dist_ts_migration/v23_aglhome.js +1775 -0
- package/dist_ts_migration/v23_runtimeconfig.d.ts +11 -0
- package/dist_ts_migration/v23_runtimeconfig.js +87 -0
- package/dist_ts_migration/v2_controllerdataroot.d.ts +5 -0
- package/dist_ts_migration/v2_controllerdataroot.js +176 -15
- package/package.json +1 -1
- package/readme.md +116 -35
- package/readme.plan.md +24 -9
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.aglhome.ts +116 -0
- package/ts/classes.authmodels.ts +5 -13
- package/ts/classes.authstore.ts +1 -39
- package/ts/classes.cli.ts +92 -34
- package/ts/classes.config.ts +20 -117
- package/ts/classes.controller.ts +92 -26
- package/ts/classes.embeddeddb.ts +5 -4
- package/ts/classes.gitreversion.ts +3 -2
- package/ts/classes.upgradecoordinator.ts +668 -191
- package/ts/classes.upgradetransaction.ts +46 -11
- package/ts/classes.uploadmanager.ts +231 -1
- package/ts/constants.upgradeenvironment.ts +2 -0
- package/ts/functions.controllerdataroot.ts +11 -47
- package/ts/functions.embeddeddb.ts +13 -2
- package/ts/functions.runtimeenvironment.ts +1 -0
- package/ts/index.ts +1 -0
- package/ts/interfaces.config.ts +5 -7
- package/ts_migration/classes.documentmigrationrunner.ts +2 -0
- package/ts_migration/index.ts +2 -0
- package/ts_migration/v23_aglhome.ts +2101 -0
- package/ts_migration/v23_runtimeconfig.ts +112 -0
- package/ts_migration/v2_controllerdataroot.ts +174 -16
- package/ts_web/00_commitinfo_data.ts +1 -1
package/ts/classes.cli.ts
CHANGED
|
@@ -7,10 +7,13 @@ import {
|
|
|
7
7
|
type IControllerStatus,
|
|
8
8
|
} from '../ts_interfaces/index.js';
|
|
9
9
|
import {
|
|
10
|
-
controllerDataDirectory,
|
|
11
10
|
defaultControllerPort,
|
|
12
11
|
} from './classes.config.js';
|
|
13
|
-
import {
|
|
12
|
+
import { resolveAGLHomePaths } from './classes.aglhome.js';
|
|
13
|
+
import {
|
|
14
|
+
ensureControllerDataRoot,
|
|
15
|
+
preflightAGLHomeMigration,
|
|
16
|
+
} from './functions.controllerdataroot.js';
|
|
14
17
|
import type { IControllerRuntimeOverrides } from './interfaces.config.js';
|
|
15
18
|
import type {
|
|
16
19
|
IControllerReadyIpcMessage,
|
|
@@ -41,8 +44,8 @@ import {
|
|
|
41
44
|
upgradePackageTransitionSource,
|
|
42
45
|
upgradePackageTransitionTarget,
|
|
43
46
|
type UpgradeStartLease,
|
|
44
|
-
upgradeTokenEnvironmentVariable,
|
|
45
47
|
} from './classes.upgradecoordinator.js';
|
|
48
|
+
import { upgradeTokenEnvironmentVariable } from './constants.upgradeenvironment.js';
|
|
46
49
|
import {
|
|
47
50
|
createUpgradeWorkerPayload,
|
|
48
51
|
launchDetachedUpgradeWorker,
|
|
@@ -82,13 +85,14 @@ Commands:
|
|
|
82
85
|
temp-password Mint a temporary browser login password (max 24 hours)
|
|
83
86
|
help Show this help
|
|
84
87
|
|
|
85
|
-
|
|
88
|
+
Startup options:
|
|
86
89
|
--port <port> Controller port (default: 4097)
|
|
87
90
|
--opencode-port <port> Local-only OpenCode port (default: 4098)
|
|
88
|
-
--directory <path>
|
|
91
|
+
--directory <path> Transiently register this existing directory on this start
|
|
92
|
+
(also valid later; omitted: do not create a project)
|
|
89
93
|
--projects-root <path> Default base for relative project paths and suggestions
|
|
90
|
-
(default:
|
|
91
|
-
be outside it;
|
|
94
|
+
(default: user home; explicitly typed absolute projects may
|
|
95
|
+
be outside it; immutable after initialization)
|
|
92
96
|
--public-origin <origin> Browser origin used for WebAuthn
|
|
93
97
|
--rp-id <hostname> WebAuthn relying-party ID
|
|
94
98
|
--behind-tls-proxy Trust an explicitly configured TLS reverse proxy
|
|
@@ -107,11 +111,10 @@ Upgrade requirements:
|
|
|
107
111
|
on Linux and macOS. Normal progress mode prints the detached worker log before
|
|
108
112
|
stopping anything; --json emits it in the terminal document.
|
|
109
113
|
|
|
110
|
-
${currentRuntimeName}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
with HARNESS_CONTROLLER_DB_DIR). Set HARNESS_CONTROLLER_MONGO_URL /
|
|
114
|
+
${currentRuntimeName} owns one private AGL_HOME (default
|
|
115
|
+
$XDG_CONFIG_HOME/agl, normally ~/.config/agl; repository checkouts use
|
|
116
|
+
./.nogit/agl). The embedded database lives under $AGL_HOME/database; override
|
|
117
|
+
the database directory with HARNESS_CONTROLLER_DB_DIR. Set HARNESS_CONTROLLER_MONGO_URL /
|
|
115
118
|
HARNESS_CONTROLLER_MONGO_DB to use an external MongoDB-compatible server
|
|
116
119
|
instead. temp-password writes to the controller database directly, so run
|
|
117
120
|
it with the same database environment as the running controller; in
|
|
@@ -120,10 +123,10 @@ password authenticates the browser UI without a passkey until it expires.
|
|
|
120
123
|
|
|
121
124
|
The first successful start prints a one-time setup code. Runtime configuration
|
|
122
125
|
is immutable after initialization; use the same --port for later management.
|
|
123
|
-
|
|
124
|
-
$
|
|
125
|
-
|
|
126
|
-
log
|
|
126
|
+
--directory is transient and may be supplied on any stopped start.
|
|
127
|
+
Detached starts append ${currentRuntimeName} errors to
|
|
128
|
+
$AGL_HOME/logs/controller-<port>.log. Upgrade workers write a separate private
|
|
129
|
+
log in $AGL_HOME/logs and print its path before work begins.
|
|
127
130
|
In --json mode the path is retained before work begins and emitted at completion.
|
|
128
131
|
`.trim();
|
|
129
132
|
|
|
@@ -258,17 +261,23 @@ const readPortOption = (
|
|
|
258
261
|
|
|
259
262
|
export const parseRuntimeOverrides = (
|
|
260
263
|
argvArg: TParsedArguments,
|
|
261
|
-
): IControllerRuntimeOverrides =>
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
264
|
+
): IControllerRuntimeOverrides => {
|
|
265
|
+
const initialProjectDirectory = readStringOption(argvArg, 'directory', 'directory');
|
|
266
|
+
const projectsRoot = readStringOption(argvArg, 'projectsRoot', 'projects-root');
|
|
267
|
+
return {
|
|
268
|
+
controllerPort: readPortOption(argvArg, 'port', 'port', defaultControllerPort)!,
|
|
269
|
+
publicOrigin: readStringOption(argvArg, 'publicOrigin', 'public-origin'),
|
|
270
|
+
rpId: readStringOption(argvArg, 'rpId', 'rp-id'),
|
|
271
|
+
initialProjectDirectory: initialProjectDirectory === undefined
|
|
272
|
+
? undefined
|
|
273
|
+
: plugins.path.resolve(initialProjectDirectory),
|
|
274
|
+
projectsRoot: projectsRoot === undefined ? undefined : plugins.path.resolve(projectsRoot),
|
|
275
|
+
opencodePort: readPortOption(argvArg, 'opencodePort', 'opencode-port'),
|
|
276
|
+
behindTlsProxy: readOption(argvArg, 'behindTlsProxy', 'behind-tls-proxy') === undefined
|
|
277
|
+
? undefined
|
|
278
|
+
: readBooleanOption(argvArg, 'behindTlsProxy', 'behind-tls-proxy'),
|
|
279
|
+
};
|
|
280
|
+
};
|
|
272
281
|
|
|
273
282
|
const currentStatusExpectation = {
|
|
274
283
|
packageName: controllerPackageName,
|
|
@@ -341,7 +350,7 @@ const detachedLogRotateBytes = 1024 * 1024;
|
|
|
341
350
|
class RetainedControllerStartupError extends Error {}
|
|
342
351
|
|
|
343
352
|
const resolveDetachedLogFilePath = (portArg: number): string => {
|
|
344
|
-
return plugins.path.join(
|
|
353
|
+
return plugins.path.join(resolveAGLHomePaths().logs, `controller-${portArg}.log`);
|
|
345
354
|
};
|
|
346
355
|
|
|
347
356
|
const openDetachedLogFile = async (
|
|
@@ -397,12 +406,13 @@ const acquireStartAdmission = async (
|
|
|
397
406
|
if (process.platform === 'win32') return undefined;
|
|
398
407
|
const installation = await tryResolveCurrentPnpmGlobalInstallation();
|
|
399
408
|
if (!installation) return undefined;
|
|
409
|
+
const upgradeToken = process.env[upgradeTokenEnvironmentVariable];
|
|
400
410
|
const coordinator = new UpgradeCoordinator(installation.globalRoot);
|
|
401
411
|
return await coordinator.acquireStartLease({
|
|
402
412
|
token: createUpgradeToken(),
|
|
403
413
|
cliPath: installation.cliPath,
|
|
404
414
|
command: commandArg,
|
|
405
|
-
upgradeToken
|
|
415
|
+
upgradeToken,
|
|
406
416
|
});
|
|
407
417
|
};
|
|
408
418
|
|
|
@@ -410,7 +420,9 @@ const serializeRuntimeOptions = (overridesArg: IControllerRuntimeOverrides): str
|
|
|
410
420
|
const args = ['--port', String(overridesArg.controllerPort)];
|
|
411
421
|
if (overridesArg.publicOrigin !== undefined) args.push('--public-origin', overridesArg.publicOrigin);
|
|
412
422
|
if (overridesArg.rpId !== undefined) args.push('--rp-id', overridesArg.rpId);
|
|
413
|
-
if (overridesArg.
|
|
423
|
+
if (overridesArg.initialProjectDirectory !== undefined) {
|
|
424
|
+
args.push('--directory', overridesArg.initialProjectDirectory);
|
|
425
|
+
}
|
|
414
426
|
if (overridesArg.projectsRoot !== undefined) args.push('--projects-root', overridesArg.projectsRoot);
|
|
415
427
|
if (overridesArg.opencodePort !== undefined) args.push('--opencode-port', String(overridesArg.opencodePort));
|
|
416
428
|
if (overridesArg.behindTlsProxy === true) args.push('--behind-tls-proxy');
|
|
@@ -609,6 +621,7 @@ const startDetachedWithoutAdmission = async (argvArg: TParsedArguments): Promise
|
|
|
609
621
|
`An ${currentRuntimeName} process is already starting or running (PID ${existingProcess.identity.pid}).`,
|
|
610
622
|
);
|
|
611
623
|
}
|
|
624
|
+
await ensureControllerDataRoot();
|
|
612
625
|
const upgradeToken = process.env[upgradeTokenEnvironmentVariable];
|
|
613
626
|
let upgradeGlobalRoot: string | undefined;
|
|
614
627
|
if (upgradeToken) {
|
|
@@ -616,7 +629,8 @@ const startDetachedWithoutAdmission = async (argvArg: TParsedArguments): Promise
|
|
|
616
629
|
}
|
|
617
630
|
const logFilePath = resolveDetachedLogFilePath(overrides.controllerPort);
|
|
618
631
|
const logFileHandle = await openDetachedLogFile(logFilePath);
|
|
619
|
-
let child: plugins.childProcess.ChildProcess;
|
|
632
|
+
let child: plugins.childProcess.ChildProcess | undefined;
|
|
633
|
+
let spawnError: unknown;
|
|
620
634
|
try {
|
|
621
635
|
const childEnvironment = { ...process.env };
|
|
622
636
|
delete childEnvironment[upgradeTokenEnvironmentVariable];
|
|
@@ -624,7 +638,7 @@ const startDetachedWithoutAdmission = async (argvArg: TParsedArguments): Promise
|
|
|
624
638
|
process.execPath,
|
|
625
639
|
[cliPath, '__serve', ...serializeRuntimeOptions(overrides)],
|
|
626
640
|
{
|
|
627
|
-
cwd:
|
|
641
|
+
cwd: plugins.os.homedir(),
|
|
628
642
|
detached: true,
|
|
629
643
|
env: childEnvironment,
|
|
630
644
|
shell: false,
|
|
@@ -633,10 +647,45 @@ const startDetachedWithoutAdmission = async (argvArg: TParsedArguments): Promise
|
|
|
633
647
|
stdio: ['ignore', 'ignore', logFileHandle.fd, 'ipc'],
|
|
634
648
|
},
|
|
635
649
|
);
|
|
636
|
-
}
|
|
650
|
+
} catch (errorArg) {
|
|
651
|
+
spawnError = errorArg;
|
|
652
|
+
}
|
|
653
|
+
let logCloseError: unknown;
|
|
654
|
+
try {
|
|
637
655
|
await logFileHandle.close();
|
|
656
|
+
} catch (errorArg) {
|
|
657
|
+
try {
|
|
658
|
+
await logFileHandle.close();
|
|
659
|
+
} catch (retryErrorArg) {
|
|
660
|
+
logCloseError = new AggregateError(
|
|
661
|
+
[errorArg, retryErrorArg],
|
|
662
|
+
'The detached controller log handle could not be closed.',
|
|
663
|
+
);
|
|
664
|
+
}
|
|
638
665
|
}
|
|
639
|
-
if (
|
|
666
|
+
if (spawnError !== undefined || logCloseError !== undefined) {
|
|
667
|
+
const errors: unknown[] = [spawnError, logCloseError]
|
|
668
|
+
.filter((errorArg) => errorArg !== undefined);
|
|
669
|
+
if (child) {
|
|
670
|
+
try {
|
|
671
|
+
if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL');
|
|
672
|
+
if (!await waitForSpawnedChildExit(child, 2_000)) {
|
|
673
|
+
throw new Error('The gated detached controller survived startup-handle cleanup.');
|
|
674
|
+
}
|
|
675
|
+
} catch (errorArg) {
|
|
676
|
+
errors.push(errorArg);
|
|
677
|
+
} finally {
|
|
678
|
+
if (child.connected) child.disconnect();
|
|
679
|
+
child.unref();
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
if (errors.length === 1) throw errors[0];
|
|
683
|
+
throw new AggregateError(
|
|
684
|
+
errors,
|
|
685
|
+
'Detached controller spawn or log-handle cleanup failed.',
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
if (!child?.pid) throw new Error('Detached controller did not provide a process ID.');
|
|
640
689
|
let spawnedIdentity: Awaited<ReturnType<typeof inspectControllerProcess>> = null;
|
|
641
690
|
try {
|
|
642
691
|
for (let attempt = 0; attempt < 20; attempt++) {
|
|
@@ -718,6 +767,7 @@ const startDetachedWithoutAdmission = async (argvArg: TParsedArguments): Promise
|
|
|
718
767
|
};
|
|
719
768
|
|
|
720
769
|
const startDetached = async (argvArg: TParsedArguments): Promise<void> => {
|
|
770
|
+
await preflightAGLHomeMigration();
|
|
721
771
|
const lease = await acquireStartAdmission('start');
|
|
722
772
|
try {
|
|
723
773
|
await startDetachedWithoutAdmission(argvArg);
|
|
@@ -727,6 +777,7 @@ const startDetached = async (argvArg: TParsedArguments): Promise<void> => {
|
|
|
727
777
|
};
|
|
728
778
|
|
|
729
779
|
const runForegroundWithAdmission = async (argvArg: TParsedArguments): Promise<void> => {
|
|
780
|
+
await preflightAGLHomeMigration();
|
|
730
781
|
const lease = await acquireStartAdmission('foreground');
|
|
731
782
|
delete process.env[upgradeTokenEnvironmentVariable];
|
|
732
783
|
try {
|
|
@@ -761,6 +812,9 @@ const runControllerProcess = async (
|
|
|
761
812
|
} else {
|
|
762
813
|
providedSetupCode = readProvidedSetupCode();
|
|
763
814
|
}
|
|
815
|
+
if (upgradeToken !== undefined) {
|
|
816
|
+
process.env[upgradeTokenEnvironmentVariable] = upgradeToken;
|
|
817
|
+
}
|
|
764
818
|
const controller = new OpenCodeController({
|
|
765
819
|
processMode: detachedArg ? 'detached' : 'foreground',
|
|
766
820
|
runtimeOverrides: parseRuntimeOverrides(argvArg),
|
|
@@ -923,6 +977,7 @@ export const createCurrentPackageUpgradeTransaction = async (
|
|
|
923
977
|
};
|
|
924
978
|
|
|
925
979
|
const upgradeController = async (argvArg: TParsedArguments): Promise<void> => {
|
|
980
|
+
await preflightAGLHomeMigration();
|
|
926
981
|
if (process.platform !== 'linux' && process.platform !== 'darwin') {
|
|
927
982
|
throw new Error(`${currentCliName} upgrade is currently supported on Linux and macOS only.`);
|
|
928
983
|
}
|
|
@@ -936,6 +991,7 @@ const upgradeController = async (argvArg: TParsedArguments): Promise<void> => {
|
|
|
936
991
|
`Port ${port} is occupied by an incompatible service or controller version; refusing to upgrade.`,
|
|
937
992
|
);
|
|
938
993
|
}
|
|
994
|
+
if (!status) await ensureControllerDataRoot();
|
|
939
995
|
const payload = createUpgradeWorkerPayload({
|
|
940
996
|
port,
|
|
941
997
|
gracePeriodMs,
|
|
@@ -1196,6 +1252,7 @@ const mintTempPasswordWithoutAdmission = async (argvArg: TParsedArguments): Prom
|
|
|
1196
1252
|
};
|
|
1197
1253
|
|
|
1198
1254
|
const mintTempPassword = async (argvArg: TParsedArguments): Promise<void> => {
|
|
1255
|
+
await preflightAGLHomeMigration();
|
|
1199
1256
|
const lease = await acquireStartAdmission('temp-password');
|
|
1200
1257
|
try {
|
|
1201
1258
|
await mintTempPasswordWithoutAdmission(argvArg);
|
|
@@ -1205,6 +1262,7 @@ const mintTempPassword = async (argvArg: TParsedArguments): Promise<void> => {
|
|
|
1205
1262
|
};
|
|
1206
1263
|
|
|
1207
1264
|
export const runCli = async (testArgvArg?: string[]): Promise<void> => {
|
|
1265
|
+
process.env.AGL_HOME = resolveAGLHomePaths().root;
|
|
1208
1266
|
activeRuntimeArguments = (testArgvArg ?? process.argv).slice(2);
|
|
1209
1267
|
if (
|
|
1210
1268
|
activeRuntimeArguments.some((argumentArg) => argumentArg === '--help' || argumentArg === '-h')
|
package/ts/classes.config.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
|
+
import {
|
|
3
|
+
resolveAGLHomePaths,
|
|
4
|
+
type IAGLHomeOptions,
|
|
5
|
+
} from './classes.aglhome.js';
|
|
2
6
|
import type {
|
|
3
7
|
IControllerDatabaseConfig,
|
|
4
8
|
IControllerRuntimeConfig,
|
|
@@ -10,22 +14,7 @@ export const defaultControllerPort = 4097;
|
|
|
10
14
|
export const defaultOpenCodePort = 4098;
|
|
11
15
|
export const trustedProxySecretEnvironmentVariable = 'HARNESS_CONTROLLER_TRUSTED_PROXY_SECRET';
|
|
12
16
|
|
|
13
|
-
export interface IControllerDataDirectoryOptions {
|
|
14
|
-
packageRoot?: string;
|
|
15
|
-
homeDirectory?: string;
|
|
16
|
-
environment?: NodeJS.ProcessEnv;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface IControllerDataRootPaths {
|
|
20
|
-
legacyRoot: string;
|
|
21
|
-
targetRoot: string;
|
|
22
|
-
migrationRequired: boolean;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const modulePackageRoot = plugins.path.resolve(
|
|
26
|
-
plugins.path.dirname(plugins.url.fileURLToPath(import.meta.url)),
|
|
27
|
-
'..',
|
|
28
|
-
);
|
|
17
|
+
export interface IControllerDataDirectoryOptions extends IAGLHomeOptions {}
|
|
29
18
|
|
|
30
19
|
const assertPort = (valueArg: number, nameArg: string): number => {
|
|
31
20
|
if (!Number.isSafeInteger(valueArg) || valueArg < 1 || valueArg > 65535) {
|
|
@@ -105,73 +94,18 @@ const resolveDirectory = async (directoryArg: string): Promise<string> => {
|
|
|
105
94
|
const resolved = await plugins.fs.promises.realpath(plugins.path.resolve(directoryArg));
|
|
106
95
|
const stats = await plugins.fs.promises.stat(resolved);
|
|
107
96
|
if (!stats.isDirectory()) {
|
|
108
|
-
throw new Error(`
|
|
97
|
+
throw new Error(`Configured path is not a directory: ${resolved}`);
|
|
109
98
|
}
|
|
110
99
|
return resolved;
|
|
111
100
|
};
|
|
112
101
|
|
|
113
|
-
const isDevelopmentCheckout = (packageRootArg: string): boolean => {
|
|
114
|
-
try {
|
|
115
|
-
const stats = plugins.fs.lstatSync(plugins.path.join(packageRootArg, '.git'));
|
|
116
|
-
return stats.isDirectory() || stats.isFile();
|
|
117
|
-
} catch {
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
const xdgRoot = (
|
|
123
|
-
environmentValueArg: string | undefined,
|
|
124
|
-
fallbackArg: string,
|
|
125
|
-
): string => {
|
|
126
|
-
const candidate = environmentValueArg?.trim();
|
|
127
|
-
return candidate && plugins.path.isAbsolute(candidate) ? candidate : fallbackArg;
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
export const controllerDataRootPaths = (
|
|
131
|
-
optionsArg: IControllerDataDirectoryOptions = {},
|
|
132
|
-
): IControllerDataRootPaths => {
|
|
133
|
-
const packageRoot = optionsArg.packageRoot ?? modulePackageRoot;
|
|
134
|
-
if (isDevelopmentCheckout(packageRoot)) {
|
|
135
|
-
const checkoutRoot = plugins.path.join(packageRoot, '.nogit');
|
|
136
|
-
return {
|
|
137
|
-
legacyRoot: checkoutRoot,
|
|
138
|
-
targetRoot: checkoutRoot,
|
|
139
|
-
migrationRequired: false,
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
const environment = optionsArg.environment ?? process.env;
|
|
143
|
-
const homeDirectory = optionsArg.homeDirectory ?? plugins.os.homedir();
|
|
144
|
-
const configHome = xdgRoot(
|
|
145
|
-
environment.XDG_CONFIG_HOME,
|
|
146
|
-
plugins.path.join(homeDirectory, '.config'),
|
|
147
|
-
);
|
|
148
|
-
return {
|
|
149
|
-
legacyRoot: plugins.path.join(configHome, 'harness-controller'),
|
|
150
|
-
targetRoot: plugins.path.join(configHome, 'hcon'),
|
|
151
|
-
migrationRequired: true,
|
|
152
|
-
};
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
/** Retained location for controller and upgrade logs across the A to B transition. */
|
|
156
102
|
export const controllerDataDirectory = (
|
|
157
103
|
optionsArg: IControllerDataDirectoryOptions = {},
|
|
158
|
-
): string =>
|
|
104
|
+
): string => resolveAGLHomePaths(optionsArg).root;
|
|
159
105
|
|
|
160
106
|
/** @deprecated Use controllerDataDirectory(). */
|
|
161
107
|
export const controllerStateDirectory = controllerDataDirectory;
|
|
162
108
|
|
|
163
|
-
const legacyControllerStateDirectory = (
|
|
164
|
-
optionsArg: IControllerDataDirectoryOptions,
|
|
165
|
-
): string => {
|
|
166
|
-
const environment = optionsArg.environment ?? process.env;
|
|
167
|
-
const homeDirectory = optionsArg.homeDirectory ?? plugins.os.homedir();
|
|
168
|
-
const stateHome = xdgRoot(
|
|
169
|
-
environment.XDG_STATE_HOME,
|
|
170
|
-
plugins.path.join(homeDirectory, '.local', 'state'),
|
|
171
|
-
);
|
|
172
|
-
return plugins.path.join(stateHome, 'harness-controller');
|
|
173
|
-
};
|
|
174
|
-
|
|
175
109
|
export const readDatabaseConfig = (
|
|
176
110
|
optionsArg: IControllerDataDirectoryOptions = {},
|
|
177
111
|
): IControllerDatabaseConfig => {
|
|
@@ -187,22 +121,13 @@ export const readDatabaseConfig = (
|
|
|
187
121
|
}
|
|
188
122
|
const embeddedDataDirectoryOverride = environment.HARNESS_CONTROLLER_DB_DIR?.trim();
|
|
189
123
|
const embeddedDataDirectory = embeddedDataDirectoryOverride
|
|
190
|
-
||
|
|
124
|
+
|| resolveAGLHomePaths(optionsArg).database;
|
|
191
125
|
if (!plugins.path.isAbsolute(embeddedDataDirectory)) {
|
|
192
126
|
throw new Error('HARNESS_CONTROLLER_DB_DIR must be an absolute path.');
|
|
193
127
|
}
|
|
194
|
-
const packageRoot = optionsArg.packageRoot ?? modulePackageRoot;
|
|
195
128
|
return {
|
|
196
129
|
mongoDbName,
|
|
197
130
|
embeddedDataDirectory,
|
|
198
|
-
...(!embeddedDataDirectoryOverride && !isDevelopmentCheckout(packageRoot)
|
|
199
|
-
? {
|
|
200
|
-
legacyEmbeddedDataDirectory: plugins.path.join(
|
|
201
|
-
legacyControllerStateDirectory(optionsArg),
|
|
202
|
-
'smartdb',
|
|
203
|
-
),
|
|
204
|
-
}
|
|
205
|
-
: {}),
|
|
206
131
|
};
|
|
207
132
|
};
|
|
208
133
|
|
|
@@ -240,17 +165,13 @@ export const resolveInitialRuntimeConfig = async (
|
|
|
240
165
|
throw new Error('Local public-origin port must match the controller listening port.');
|
|
241
166
|
}
|
|
242
167
|
const rpId = normalizeRpId(overridesArg.rpId ?? origin.hostname, origin.hostname);
|
|
243
|
-
const workspaceDirectory = await resolveDirectory(
|
|
244
|
-
overridesArg.workspaceDirectory ?? process.cwd(),
|
|
245
|
-
);
|
|
246
168
|
const projectsRoot = await resolveDirectory(
|
|
247
|
-
overridesArg.projectsRoot ??
|
|
169
|
+
overridesArg.projectsRoot ?? plugins.os.homedir(),
|
|
248
170
|
);
|
|
249
171
|
return {
|
|
250
172
|
controllerPort,
|
|
251
173
|
publicOrigin: origin.origin,
|
|
252
174
|
rpId,
|
|
253
|
-
workspaceDirectory,
|
|
254
175
|
projectsRoot,
|
|
255
176
|
opencodePort,
|
|
256
177
|
tlsMode,
|
|
@@ -308,16 +229,6 @@ export const resolveStoredRuntimeConfig = async (
|
|
|
308
229
|
if (overridesArg.opencodePort !== undefined && overridesArg.opencodePort !== storedArg.opencodePort) {
|
|
309
230
|
throw new Error('opencodePort is immutable after controller initialization.');
|
|
310
231
|
}
|
|
311
|
-
if (overridesArg.workspaceDirectory !== undefined) {
|
|
312
|
-
const requestedDirectory = await resolveDirectory(overridesArg.workspaceDirectory);
|
|
313
|
-
if (requestedDirectory !== storedArg.workspaceDirectory) {
|
|
314
|
-
throw new Error('The OpenCode workspace is immutable after controller initialization.');
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
const storedDirectory = await resolveDirectory(storedArg.workspaceDirectory);
|
|
318
|
-
if (storedDirectory !== storedArg.workspaceDirectory) {
|
|
319
|
-
throw new Error('Stored OpenCode workspace is no longer its canonical real path.');
|
|
320
|
-
}
|
|
321
232
|
if (storedArg.tlsMode === 'proxy') {
|
|
322
233
|
if (overridesArg.behindTlsProxy === false) {
|
|
323
234
|
throw new Error('Stored TLS proxy mode is immutable.');
|
|
@@ -327,25 +238,17 @@ export const resolveStoredRuntimeConfig = async (
|
|
|
327
238
|
throw new Error('Stored localhost mode cannot be changed to TLS mode.');
|
|
328
239
|
}
|
|
329
240
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
if (projectsRoot !== storedArg.projectsRoot) {
|
|
341
|
-
throw new Error('Stored projects root is no longer its canonical real path.');
|
|
342
|
-
}
|
|
343
|
-
if (overridesArg.projectsRoot !== undefined) {
|
|
344
|
-
const requestedProjectsRoot = await resolveDirectory(overridesArg.projectsRoot);
|
|
345
|
-
if (requestedProjectsRoot !== storedArg.projectsRoot) {
|
|
346
|
-
throw new Error('The projects root is immutable after it has been adopted.');
|
|
347
|
-
}
|
|
241
|
+
if (
|
|
242
|
+
!plugins.path.isAbsolute(storedArg.projectsRoot)
|
|
243
|
+
|| plugins.path.normalize(storedArg.projectsRoot) !== storedArg.projectsRoot
|
|
244
|
+
) {
|
|
245
|
+
throw new Error('Stored projects root is not an absolute normalized path.');
|
|
246
|
+
}
|
|
247
|
+
if (overridesArg.projectsRoot !== undefined) {
|
|
248
|
+
const requestedProjectsRoot = await resolveDirectory(overridesArg.projectsRoot);
|
|
249
|
+
if (requestedProjectsRoot !== storedArg.projectsRoot) {
|
|
250
|
+
throw new Error('The projects root is immutable after it has been adopted.');
|
|
348
251
|
}
|
|
349
252
|
}
|
|
350
|
-
return { ...storedArg
|
|
253
|
+
return { ...storedArg };
|
|
351
254
|
};
|