agl 1.0.17 → 21.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/.smartconfig.json +55 -0
- package/changelog.md +1026 -0
- package/cli.js +14 -0
- package/dist_serve/bundle.js +44778 -0
- package/dist_serve/index.html +13 -0
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/classes.authmodels.d.ts +126 -0
- package/dist_ts/classes.authmodels.js +1422 -0
- package/dist_ts/classes.authstore.d.ts +160 -0
- package/dist_ts/classes.authstore.js +2483 -0
- package/dist_ts/classes.browserresourcehost.d.ts +85 -0
- package/dist_ts/classes.browserresourcehost.js +912 -0
- package/dist_ts/classes.cli.d.ts +21 -0
- package/dist_ts/classes.cli.js +1082 -0
- package/dist_ts/classes.config.d.ts +23 -0
- package/dist_ts/classes.config.js +267 -0
- package/dist_ts/classes.controller.d.ts +523 -0
- package/dist_ts/classes.controller.js +10611 -0
- package/dist_ts/classes.controllermanagement.d.ts +32 -0
- package/dist_ts/classes.controllermanagement.js +222 -0
- package/dist_ts/classes.draftmanager.d.ts +30 -0
- package/dist_ts/classes.draftmanager.js +179 -0
- package/dist_ts/classes.embeddeddb.d.ts +30 -0
- package/dist_ts/classes.embeddeddb.js +159 -0
- package/dist_ts/classes.flexframedtransport.d.ts +56 -0
- package/dist_ts/classes.flexframedtransport.js +275 -0
- package/dist_ts/classes.flexmodels.private.d.ts +231 -0
- package/dist_ts/classes.flexmodels.private.js +985 -0
- package/dist_ts/classes.flexmodels.public.d.ts +207 -0
- package/dist_ts/classes.flexmodels.public.js +885 -0
- package/dist_ts/classes.flexprojectionreader.d.ts +89 -0
- package/dist_ts/classes.flexprojectionreader.js +580 -0
- package/dist_ts/classes.flexservice.d.ts +196 -0
- package/dist_ts/classes.flexservice.js +3704 -0
- package/dist_ts/classes.flexstore.d.ts +174 -0
- package/dist_ts/classes.flexstore.js +3400 -0
- package/dist_ts/classes.flexsupervisor.d.ts +105 -0
- package/dist_ts/classes.flexsupervisor.js +1191 -0
- package/dist_ts/classes.gitreversion.d.ts +431 -0
- package/dist_ts/classes.gitreversion.js +10592 -0
- package/dist_ts/classes.harnessoperationmanager.d.ts +113 -0
- package/dist_ts/classes.harnessoperationmanager.js +602 -0
- package/dist_ts/classes.opencodeclient.d.ts +146 -0
- package/dist_ts/classes.opencodeclient.js +3003 -0
- package/dist_ts/classes.opencodesupervisor.d.ts +60 -0
- package/dist_ts/classes.opencodesupervisor.js +1003 -0
- package/dist_ts/classes.passkeymanager.d.ts +34 -0
- package/dist_ts/classes.passkeymanager.js +456 -0
- package/dist_ts/classes.processinspection.d.ts +50 -0
- package/dist_ts/classes.processinspection.js +484 -0
- package/dist_ts/classes.resourcecoordinator.d.ts +64 -0
- package/dist_ts/classes.resourcecoordinator.js +294 -0
- package/dist_ts/classes.sessionintelligencereader.d.ts +13 -0
- package/dist_ts/classes.sessionintelligencereader.js +40 -0
- package/dist_ts/classes.systemmetricssampler.d.ts +53 -0
- package/dist_ts/classes.systemmetricssampler.js +168 -0
- package/dist_ts/classes.terminalmanager.d.ts +95 -0
- package/dist_ts/classes.terminalmanager.js +981 -0
- package/dist_ts/classes.terminalresourcehost.d.ts +20 -0
- package/dist_ts/classes.terminalresourcehost.js +84 -0
- package/dist_ts/classes.upgradecoordinator.d.ts +244 -0
- package/dist_ts/classes.upgradecoordinator.js +2155 -0
- package/dist_ts/classes.upgradetransaction.d.ts +92 -0
- package/dist_ts/classes.upgradetransaction.js +2125 -0
- package/dist_ts/classes.uploadmanager.d.ts +24 -0
- package/dist_ts/classes.uploadmanager.js +210 -0
- package/dist_ts/classes.validation.d.ts +91 -0
- package/dist_ts/classes.validation.js +1087 -0
- package/dist_ts/flexharness.child.d.ts +1 -0
- package/dist_ts/flexharness.child.js +28 -0
- package/dist_ts/functions.canonicaldirectory.d.ts +21 -0
- package/dist_ts/functions.canonicaldirectory.js +144 -0
- package/dist_ts/functions.controllerdataroot.d.ts +15 -0
- package/dist_ts/functions.controllerdataroot.js +76 -0
- package/dist_ts/functions.embeddeddb.d.ts +2 -0
- package/dist_ts/functions.embeddeddb.js +21 -0
- package/dist_ts/functions.flexcredentialstore.d.ts +16 -0
- package/dist_ts/functions.flexcredentialstore.js +93 -0
- package/dist_ts/functions.opencodeauth.d.ts +3 -0
- package/dist_ts/functions.opencodeauth.js +31 -0
- package/dist_ts/functions.runtimeenvironment.d.ts +2 -0
- package/dist_ts/functions.runtimeenvironment.js +79 -0
- package/dist_ts/functions.upgradefollower.d.ts +3 -0
- package/dist_ts/functions.upgradefollower.js +95 -0
- package/dist_ts/index.d.ts +24 -0
- package/dist_ts/index.js +25 -0
- package/dist_ts/interfaces.auth.d.ts +181 -0
- package/dist_ts/interfaces.auth.js +82 -0
- package/dist_ts/interfaces.config.d.ts +34 -0
- package/dist_ts/interfaces.config.js +2 -0
- package/dist_ts/interfaces.flexipc.d.ts +939 -0
- package/dist_ts/interfaces.flexipc.js +1492 -0
- package/dist_ts/interfaces.gitreversion.d.ts +246 -0
- package/dist_ts/interfaces.gitreversion.js +2 -0
- package/dist_ts/interfaces.lifecycle.d.ts +37 -0
- package/dist_ts/interfaces.lifecycle.js +2 -0
- package/dist_ts/interfaces.opencode.d.ts +101 -0
- package/dist_ts/interfaces.opencode.js +3 -0
- package/dist_ts/interfaces.projects.d.ts +156 -0
- package/dist_ts/interfaces.projects.js +5 -0
- package/dist_ts/plugins.d.ts +36 -0
- package/dist_ts/plugins.js +40 -0
- package/dist_ts_interfaces/index.d.ts +2 -0
- package/dist_ts_interfaces/index.js +3 -0
- package/dist_ts_interfaces/interfaces.d.ts +747 -0
- package/dist_ts_interfaces/interfaces.js +47 -0
- package/dist_ts_interfaces/plugins.d.ts +4 -0
- package/dist_ts_interfaces/plugins.js +3 -0
- package/dist_ts_interfaces/requests.d.ts +806 -0
- package/dist_ts_interfaces/requests.js +2 -0
- package/dist_ts_migration/classes.documentmigrationrunner.d.ts +8 -0
- package/dist_ts_migration/classes.documentmigrationrunner.js +24 -0
- package/dist_ts_migration/classes.flexmigrationrunner.d.ts +11 -0
- package/dist_ts_migration/classes.flexmigrationrunner.js +25 -0
- package/dist_ts_migration/classes.flexpostharnessmigrationrunner.d.ts +10 -0
- package/dist_ts_migration/classes.flexpostharnessmigrationrunner.js +15 -0
- package/dist_ts_migration/classes.migrationrunner.d.ts +8 -0
- package/dist_ts_migration/classes.migrationrunner.js +34 -0
- package/dist_ts_migration/index.d.ts +12 -0
- package/dist_ts_migration/index.js +13 -0
- package/dist_ts_migration/v10_flexharnessv3migration.d.ts +11 -0
- package/dist_ts_migration/v10_flexharnessv3migration.js +23 -0
- package/dist_ts_migration/v13_flexprojectionorder.d.ts +11 -0
- package/dist_ts_migration/v13_flexprojectionorder.js +23 -0
- package/dist_ts_migration/v16_flexprovidercredentials.d.ts +22 -0
- package/dist_ts_migration/v16_flexprovidercredentials.js +149 -0
- package/dist_ts_migration/v18_resourcelayout.d.ts +22 -0
- package/dist_ts_migration/v18_resourcelayout.js +23 -0
- package/dist_ts_migration/v18_resourcelayoutmigration.d.ts +6 -0
- package/dist_ts_migration/v18_resourcelayoutmigration.js +61 -0
- package/dist_ts_migration/v19_flexprojectmanagement.d.ts +11 -0
- package/dist_ts_migration/v19_flexprojectmanagement.js +21 -0
- package/dist_ts_migration/v1_embeddeddatabaselocation.d.ts +28 -0
- package/dist_ts_migration/v1_embeddeddatabaselocation.js +415 -0
- package/dist_ts_migration/v22_projectfilesystemidentitymigration.d.ts +7 -0
- package/dist_ts_migration/v22_projectfilesystemidentitymigration.js +105 -0
- package/dist_ts_migration/v2_controllerdataroot.d.ts +130 -0
- package/dist_ts_migration/v2_controllerdataroot.js +1887 -0
- package/dist_ts_migration/v6_protocoldocumentmigration.d.ts +7 -0
- package/dist_ts_migration/v6_protocoldocumentmigration.js +100 -0
- package/dist_ts_migration/v6_protocoldocuments.d.ts +7 -0
- package/dist_ts_migration/v6_protocoldocuments.js +138 -0
- package/html/index.html +13 -0
- package/{LICENSE → license.md} +2 -2
- package/package.json +86 -48
- package/readme.md +480 -0
- package/readme.plan.md +188 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/classes.authmodels.ts +1245 -0
- package/ts/classes.authstore.ts +3274 -0
- package/ts/classes.browserresourcehost.ts +1205 -0
- package/ts/classes.cli.ts +1254 -0
- package/ts/classes.config.ts +351 -0
- package/ts/classes.controller.ts +14426 -0
- package/ts/classes.controllermanagement.ts +330 -0
- package/ts/classes.draftmanager.ts +232 -0
- package/ts/classes.embeddeddb.ts +166 -0
- package/ts/classes.flexframedtransport.ts +283 -0
- package/ts/classes.flexmodels.private.ts +823 -0
- package/ts/classes.flexmodels.public.ts +732 -0
- package/ts/classes.flexprojectionreader.ts +816 -0
- package/ts/classes.flexservice.ts +4302 -0
- package/ts/classes.flexstore.ts +4565 -0
- package/ts/classes.flexsupervisor.ts +1453 -0
- package/ts/classes.gitreversion.ts +13353 -0
- package/ts/classes.harnessoperationmanager.ts +870 -0
- package/ts/classes.opencodeclient.ts +4110 -0
- package/ts/classes.opencodesupervisor.ts +1280 -0
- package/ts/classes.passkeymanager.ts +570 -0
- package/ts/classes.processinspection.ts +623 -0
- package/ts/classes.resourcecoordinator.ts +436 -0
- package/ts/classes.sessionintelligencereader.ts +52 -0
- package/ts/classes.systemmetricssampler.ts +215 -0
- package/ts/classes.terminalmanager.ts +1207 -0
- package/ts/classes.terminalresourcehost.ts +122 -0
- package/ts/classes.upgradecoordinator.ts +2675 -0
- package/ts/classes.upgradetransaction.ts +2900 -0
- package/ts/classes.uploadmanager.ts +240 -0
- package/ts/classes.validation.ts +1409 -0
- package/ts/flexharness.child.ts +35 -0
- package/ts/functions.canonicaldirectory.ts +213 -0
- package/ts/functions.controllerdataroot.ts +107 -0
- package/ts/functions.embeddeddb.ts +22 -0
- package/ts/functions.flexcredentialstore.ts +128 -0
- package/ts/functions.opencodeauth.ts +34 -0
- package/ts/functions.runtimeenvironment.ts +90 -0
- package/ts/functions.upgradefollower.ts +115 -0
- package/ts/index.ts +36 -6
- package/ts/interfaces.auth.ts +316 -0
- package/ts/interfaces.config.ts +37 -0
- package/ts/interfaces.flexipc.ts +2586 -0
- package/ts/interfaces.gitreversion.ts +336 -0
- package/ts/interfaces.lifecycle.ts +46 -0
- package/ts/interfaces.opencode.ts +153 -0
- package/ts/interfaces.projects.ts +182 -0
- package/ts/plugins.ts +64 -0
- package/ts_interfaces/index.ts +2 -0
- package/ts_interfaces/interfaces.ts +827 -0
- package/ts_interfaces/plugins.ts +15 -0
- package/ts_interfaces/requests.ts +1186 -0
- package/ts_migration/classes.documentmigrationrunner.ts +32 -0
- package/ts_migration/classes.flexmigrationrunner.ts +33 -0
- package/ts_migration/classes.flexpostharnessmigrationrunner.ts +22 -0
- package/ts_migration/classes.migrationrunner.ts +40 -0
- package/ts_migration/index.ts +12 -0
- package/ts_migration/v10_flexharnessv3migration.ts +35 -0
- package/ts_migration/v13_flexprojectionorder.ts +33 -0
- package/ts_migration/v16_flexprovidercredentials.ts +196 -0
- package/ts_migration/v18_resourcelayout.ts +55 -0
- package/ts_migration/v18_resourcelayoutmigration.ts +68 -0
- package/ts_migration/v19_flexprojectmanagement.ts +28 -0
- package/ts_migration/v1_embeddeddatabaselocation.ts +560 -0
- package/ts_migration/v22_projectfilesystemidentitymigration.ts +121 -0
- package/ts_migration/v2_controllerdataroot.ts +2349 -0
- package/ts_migration/v6_protocoldocumentmigration.ts +144 -0
- package/ts_migration/v6_protocoldocuments.ts +198 -0
- package/ts_web/00_commitinfo_data.ts +8 -0
- package/ts_web/classes.controllersocketclient.ts +517 -0
- package/ts_web/classes.sessiondraftsync.ts +390 -0
- package/ts_web/elements.harnesscontrollerapp.ts +11494 -0
- package/ts_web/index.ts +2 -0
- package/ts_web/plugins.ts +31 -0
- package/.gitlab-ci.yml +0 -71
- package/.npmignore +0 -5
- package/README.md +0 -40
- package/dist/cli.js +0 -2
- package/dist/index.d.ts +0 -0
- package/dist/index.js +0 -9
- package/dist/mod_bundle/index.d.ts +0 -1
- package/dist/mod_bundle/index.js +0 -18
- package/dist/mod_bundle/mod.plugins.d.ts +0 -4
- package/dist/mod_bundle/mod.plugins.js +0 -11
- package/dist/mod_install/index.d.ts +0 -4
- package/dist/mod_install/index.js +0 -21
- package/dist/mod_install/mod.plugins.d.ts +0 -1
- package/dist/mod_install/mod.plugins.js +0 -7
- package/dist/mod_standard/index.d.ts +0 -4
- package/dist/mod_standard/index.js +0 -21
- package/dist/mod_standard/mod.browser.d.ts +0 -6
- package/dist/mod_standard/mod.browser.js +0 -14
- package/dist/mod_standard/mod.clean.d.ts +0 -4
- package/dist/mod_standard/mod.clean.js +0 -12
- package/dist/mod_standard/mod.compile.d.ts +0 -4
- package/dist/mod_standard/mod.compile.js +0 -21
- package/dist/mod_standard/mod.plugins.d.ts +0 -8
- package/dist/mod_standard/mod.plugins.js +0 -19
- package/dist/mod_standard/mod.test.d.ts +0 -4
- package/dist/mod_standard/mod.test.js +0 -12
- package/dist/plr.cli.d.ts +0 -3
- package/dist/plr.cli.js +0 -57
- package/dist/plr.config.d.ts +0 -11
- package/dist/plr.config.js +0 -26
- package/dist/plr.mods.d.ts +0 -17
- package/dist/plr.mods.js +0 -17
- package/dist/plr.paths.d.ts +0 -19
- package/dist/plr.paths.js +0 -26
- package/dist/plr.plugins.d.ts +0 -8
- package/dist/plr.plugins.js +0 -16
- package/npmextra.json +0 -10
- package/ts/mod_bundle/index.ts +0 -20
- package/ts/mod_bundle/mod.plugins.ts +0 -9
- package/ts/mod_install/index.ts +0 -20
- package/ts/mod_install/mod.plugins.ts +0 -1
- package/ts/mod_standard/index.ts +0 -23
- package/ts/mod_standard/mod.browser.ts +0 -17
- package/ts/mod_standard/mod.clean.ts +0 -13
- package/ts/mod_standard/mod.compile.ts +0 -23
- package/ts/mod_standard/mod.plugins.ts +0 -17
- package/ts/mod_standard/mod.test.ts +0 -13
- package/ts/plr.cli.ts +0 -60
- package/ts/plr.config.ts +0 -36
- package/ts/plr.mods.ts +0 -25
- package/ts/plr.paths.ts +0 -29
- package/ts/plr.plugins.ts +0 -17
- package/tslint.json +0 -3
- package/yarn.lock +0 -3464
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import { FlexFramedTransport } from './classes.flexframedtransport.js';
|
|
3
|
+
import { FlexService } from './classes.flexservice.js';
|
|
4
|
+
import {
|
|
5
|
+
type TFlexChildMessage,
|
|
6
|
+
type TFlexParentMessage,
|
|
7
|
+
parseFlexChildMessage,
|
|
8
|
+
parseFlexParentMessage,
|
|
9
|
+
} from './interfaces.flexipc.js';
|
|
10
|
+
|
|
11
|
+
const readable = plugins.fs.createReadStream('', { fd: 3, autoClose: false });
|
|
12
|
+
const writable = plugins.fs.createWriteStream('', { fd: 4, autoClose: false });
|
|
13
|
+
const transport = new FlexFramedTransport<TFlexParentMessage, TFlexChildMessage>({
|
|
14
|
+
readable,
|
|
15
|
+
writable,
|
|
16
|
+
parseIncoming: parseFlexParentMessage,
|
|
17
|
+
validateOutgoing: parseFlexChildMessage,
|
|
18
|
+
});
|
|
19
|
+
const service = new FlexService({ transport });
|
|
20
|
+
|
|
21
|
+
let shutdownStarted = false;
|
|
22
|
+
const shutdown = (): void => {
|
|
23
|
+
if (shutdownStarted) return;
|
|
24
|
+
shutdownStarted = true;
|
|
25
|
+
void service.dispose().finally(() => {
|
|
26
|
+
process.exitCode = 0;
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
process.once('SIGTERM', shutdown);
|
|
31
|
+
process.once('SIGINT', shutdown);
|
|
32
|
+
process.once('uncaughtException', shutdown);
|
|
33
|
+
process.once('unhandledRejection', shutdown);
|
|
34
|
+
|
|
35
|
+
await service.start();
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import {
|
|
3
|
+
controllerFilesystemIdentityMaximumComponents,
|
|
4
|
+
type IControllerFilesystemIdentity,
|
|
5
|
+
type IControllerFilesystemIdentityComponent,
|
|
6
|
+
type IControllerProjectFilesystemIdentity,
|
|
7
|
+
type TControllerFilesystemObjectType,
|
|
8
|
+
} from './interfaces.projects.js';
|
|
9
|
+
|
|
10
|
+
export class CanonicalDirectoryError extends Error {
|
|
11
|
+
constructor(messageArg: string, optionsArg?: ErrorOptions) {
|
|
12
|
+
super(messageArg, optionsArg);
|
|
13
|
+
this.name = 'CanonicalDirectoryError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ICanonicalDirectoryResolution extends IControllerProjectFilesystemIdentity {
|
|
18
|
+
directory: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ICanonicalFilesystemObjectResolution extends IControllerFilesystemIdentity {
|
|
22
|
+
path: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ICanonicalFilesystemObjectExpectation {
|
|
26
|
+
path: string;
|
|
27
|
+
targetType: TControllerFilesystemObjectType;
|
|
28
|
+
expectedIdentity: IControllerFilesystemIdentity;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface ICanonicalFilesystemObjectRequest {
|
|
32
|
+
path: string;
|
|
33
|
+
targetType: TControllerFilesystemObjectType;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const filesystemIdentityComponent = (
|
|
37
|
+
statArg: plugins.fs.BigIntStats,
|
|
38
|
+
): IControllerFilesystemIdentityComponent => ({
|
|
39
|
+
device: statArg.dev.toString(10),
|
|
40
|
+
inode: statArg.ino.toString(10),
|
|
41
|
+
type: statArg.isDirectory() ? 'directory' : 'file',
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const pathComponents = (pathArg: string): string[] => {
|
|
45
|
+
if (
|
|
46
|
+
!plugins.path.isAbsolute(pathArg)
|
|
47
|
+
|| pathArg.includes('\0')
|
|
48
|
+
|| plugins.path.normalize(pathArg) !== pathArg
|
|
49
|
+
) {
|
|
50
|
+
throw new CanonicalDirectoryError('The filesystem path is not canonical and absolute.');
|
|
51
|
+
}
|
|
52
|
+
const root = plugins.path.parse(pathArg).root;
|
|
53
|
+
const relative = pathArg.slice(root.length);
|
|
54
|
+
const components = [
|
|
55
|
+
root,
|
|
56
|
+
...relative.split(plugins.path.sep).filter((component) => component.length > 0),
|
|
57
|
+
];
|
|
58
|
+
if (components.length > controllerFilesystemIdentityMaximumComponents) {
|
|
59
|
+
throw new CanonicalDirectoryError('The filesystem path exceeds the identity component limit.');
|
|
60
|
+
}
|
|
61
|
+
return components;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const componentPaths = (pathArg: string): string[] => {
|
|
65
|
+
const components = pathComponents(pathArg);
|
|
66
|
+
const paths = [components[0]!];
|
|
67
|
+
for (const component of components.slice(1)) {
|
|
68
|
+
paths.push(plugins.path.join(paths.at(-1)!, component));
|
|
69
|
+
}
|
|
70
|
+
return paths;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const statComponentPath = async (
|
|
74
|
+
pathArg: string,
|
|
75
|
+
targetTypeArg: TControllerFilesystemObjectType,
|
|
76
|
+
): Promise<IControllerFilesystemIdentityComponent> => {
|
|
77
|
+
const stat = await plugins.fs.promises.lstat(pathArg, { bigint: true });
|
|
78
|
+
if (stat.isSymbolicLink()) {
|
|
79
|
+
throw new CanonicalDirectoryError('The filesystem path contains a symbolic link.');
|
|
80
|
+
}
|
|
81
|
+
const type = stat.isDirectory() ? 'directory' : stat.isFile() ? 'file' : undefined;
|
|
82
|
+
if (!type || type !== targetTypeArg) {
|
|
83
|
+
throw new CanonicalDirectoryError(`The filesystem path is not a canonical ${targetTypeArg}.`);
|
|
84
|
+
}
|
|
85
|
+
return filesystemIdentityComponent(stat);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const filesystemIdentitiesEqual = (
|
|
89
|
+
leftArg: IControllerFilesystemIdentity,
|
|
90
|
+
rightArg: IControllerFilesystemIdentity,
|
|
91
|
+
): boolean => leftArg.ancestry.length === rightArg.ancestry.length
|
|
92
|
+
&& leftArg.ancestry.every((component, index) => {
|
|
93
|
+
const other = rightArg.ancestry[index];
|
|
94
|
+
return component.device === other?.device
|
|
95
|
+
&& component.inode === other.inode
|
|
96
|
+
&& component.type === other.type;
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const resolveCanonicalFilesystemObjects = async (
|
|
100
|
+
requestsArg: readonly ICanonicalFilesystemObjectRequest[],
|
|
101
|
+
): Promise<ICanonicalFilesystemObjectResolution[]> => {
|
|
102
|
+
try {
|
|
103
|
+
const requests = requestsArg.map((request) => ({
|
|
104
|
+
...request,
|
|
105
|
+
componentPaths: componentPaths(request.path),
|
|
106
|
+
}));
|
|
107
|
+
const componentTypes = new Map<string, TControllerFilesystemObjectType>();
|
|
108
|
+
for (const request of requests) {
|
|
109
|
+
for (let index = 0; index < request.componentPaths.length; index += 1) {
|
|
110
|
+
const path = request.componentPaths[index]!;
|
|
111
|
+
const type = index === request.componentPaths.length - 1
|
|
112
|
+
? request.targetType
|
|
113
|
+
: 'directory';
|
|
114
|
+
const existing = componentTypes.get(path);
|
|
115
|
+
if (existing && existing !== type) {
|
|
116
|
+
throw new CanonicalDirectoryError(
|
|
117
|
+
'Overlapping filesystem paths require conflicting object types.',
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
componentTypes.set(path, type);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const collect = async (): Promise<Map<string, IControllerFilesystemIdentityComponent>> => {
|
|
124
|
+
const identities = new Map<string, IControllerFilesystemIdentityComponent>();
|
|
125
|
+
for (const [path, type] of componentTypes) {
|
|
126
|
+
identities.set(path, await statComponentPath(path, type));
|
|
127
|
+
}
|
|
128
|
+
return identities;
|
|
129
|
+
};
|
|
130
|
+
const before = await collect();
|
|
131
|
+
const after = await collect();
|
|
132
|
+
for (const [path, beforeIdentity] of before) {
|
|
133
|
+
const afterIdentity = after.get(path)!;
|
|
134
|
+
if (!filesystemIdentitiesEqual(
|
|
135
|
+
{ ancestry: [beforeIdentity] },
|
|
136
|
+
{ ancestry: [afterIdentity] },
|
|
137
|
+
)) {
|
|
138
|
+
throw new CanonicalDirectoryError(
|
|
139
|
+
'The filesystem ancestry changed during identity validation.',
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return requests.map((request) => ({
|
|
144
|
+
path: request.path,
|
|
145
|
+
ancestry: request.componentPaths.map((path) => after.get(path)!),
|
|
146
|
+
}));
|
|
147
|
+
} catch (errorArg) {
|
|
148
|
+
if (errorArg instanceof CanonicalDirectoryError) throw errorArg;
|
|
149
|
+
throw new CanonicalDirectoryError('The canonical filesystem path is unavailable.', {
|
|
150
|
+
cause: errorArg,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export const resolveCanonicalFilesystemObject = async (
|
|
156
|
+
pathArg: string,
|
|
157
|
+
targetTypeArg: TControllerFilesystemObjectType,
|
|
158
|
+
): Promise<ICanonicalFilesystemObjectResolution> => {
|
|
159
|
+
return (await resolveCanonicalFilesystemObjects([
|
|
160
|
+
{ path: pathArg, targetType: targetTypeArg },
|
|
161
|
+
]))[0]!;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export const assertCanonicalFilesystemObject = async (
|
|
165
|
+
pathArg: string,
|
|
166
|
+
targetTypeArg: TControllerFilesystemObjectType,
|
|
167
|
+
expectedIdentityArg: IControllerFilesystemIdentity,
|
|
168
|
+
): Promise<string> => {
|
|
169
|
+
const resolved = await resolveCanonicalFilesystemObject(pathArg, targetTypeArg);
|
|
170
|
+
if (!filesystemIdentitiesEqual(resolved, expectedIdentityArg)) {
|
|
171
|
+
throw new CanonicalDirectoryError(
|
|
172
|
+
'The filesystem ancestry no longer matches its registered identity.',
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
return resolved.path;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
export const assertCanonicalFilesystemObjects = async (
|
|
179
|
+
expectationsArg: readonly ICanonicalFilesystemObjectExpectation[],
|
|
180
|
+
): Promise<void> => {
|
|
181
|
+
try {
|
|
182
|
+
const resolved = await resolveCanonicalFilesystemObjects(expectationsArg);
|
|
183
|
+
for (let index = 0; index < expectationsArg.length; index += 1) {
|
|
184
|
+
if (!filesystemIdentitiesEqual(resolved[index]!, expectationsArg[index]!.expectedIdentity)) {
|
|
185
|
+
throw new CanonicalDirectoryError(
|
|
186
|
+
'The filesystem ancestry no longer matches its registered identity.',
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
} catch (errorArg) {
|
|
191
|
+
if (errorArg instanceof CanonicalDirectoryError) throw errorArg;
|
|
192
|
+
throw new CanonicalDirectoryError('The canonical filesystem path is unavailable.', {
|
|
193
|
+
cause: errorArg,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
export const resolveCanonicalDirectory = async (
|
|
199
|
+
directoryArg: string,
|
|
200
|
+
): Promise<ICanonicalDirectoryResolution> => {
|
|
201
|
+
const resolved = await resolveCanonicalFilesystemObject(directoryArg, 'directory');
|
|
202
|
+
return { directory: resolved.path, ancestry: resolved.ancestry };
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
export const assertCanonicalDirectory = async (
|
|
206
|
+
directoryArg: string,
|
|
207
|
+
expectedIdentityArg?: IControllerProjectFilesystemIdentity,
|
|
208
|
+
): Promise<string> => {
|
|
209
|
+
if (expectedIdentityArg) {
|
|
210
|
+
return assertCanonicalFilesystemObject(directoryArg, 'directory', expectedIdentityArg);
|
|
211
|
+
}
|
|
212
|
+
return (await resolveCanonicalDirectory(directoryArg)).directory;
|
|
213
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import {
|
|
3
|
+
controllerDataRootPaths,
|
|
4
|
+
readDatabaseConfig,
|
|
5
|
+
type IControllerDataDirectoryOptions,
|
|
6
|
+
} from './classes.config.js';
|
|
7
|
+
import {
|
|
8
|
+
listControllerDataWriterProcessesForCliPaths,
|
|
9
|
+
} from './classes.processinspection.js';
|
|
10
|
+
import { embeddedDatabaseSocketPath } from './functions.embeddeddb.js';
|
|
11
|
+
import {
|
|
12
|
+
ControllerDataRootMigrationRunner,
|
|
13
|
+
type IControllerDataRootMigrationResult,
|
|
14
|
+
type IControllerDataWriterProcessRecord,
|
|
15
|
+
} from '../ts_migration/v2_controllerdataroot.js';
|
|
16
|
+
|
|
17
|
+
export interface IEnsureControllerDataRootOptions extends IControllerDataDirectoryOptions {
|
|
18
|
+
listDataWriterProcesses?: () => Promise<readonly IControllerDataWriterProcessRecord[]>;
|
|
19
|
+
signal?: AbortSignal;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const ensureControllerDataRoot = async (
|
|
23
|
+
optionsArg: IEnsureControllerDataRootOptions = {},
|
|
24
|
+
): Promise<IControllerDataRootMigrationResult> => {
|
|
25
|
+
const roots = controllerDataRootPaths(optionsArg);
|
|
26
|
+
const databaseConfig = readDatabaseConfig(optionsArg);
|
|
27
|
+
if (!roots.migrationRequired) {
|
|
28
|
+
return { directoryPath: roots.targetRoot, databaseConfig };
|
|
29
|
+
}
|
|
30
|
+
const listDataWriterProcesses = optionsArg.listDataWriterProcesses ?? (async () => {
|
|
31
|
+
const cliPath = await plugins.fs.promises.realpath(
|
|
32
|
+
plugins.url.fileURLToPath(new URL('../cli.js', import.meta.url)),
|
|
33
|
+
);
|
|
34
|
+
return await listControllerDataWriterProcessesForCliPaths([cliPath], {
|
|
35
|
+
includeInstalledPackagePaths: true,
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
return await new ControllerDataRootMigrationRunner({
|
|
39
|
+
oldRoot: roots.legacyRoot,
|
|
40
|
+
newRoot: roots.targetRoot,
|
|
41
|
+
databaseConfig,
|
|
42
|
+
oldEmbeddedSocketPath: embeddedDatabaseSocketPath(
|
|
43
|
+
plugins.path.join(roots.legacyRoot, 'smartdb'),
|
|
44
|
+
),
|
|
45
|
+
newEmbeddedSocketPath: embeddedDatabaseSocketPath(
|
|
46
|
+
plugins.path.join(roots.targetRoot, 'smartdb'),
|
|
47
|
+
),
|
|
48
|
+
invokerPid: process.pid,
|
|
49
|
+
listDataWriterProcesses,
|
|
50
|
+
...(optionsArg.signal ? { signal: optionsArg.signal } : {}),
|
|
51
|
+
}).run();
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export interface IEnsureControllerDataRootForStartupOptions
|
|
55
|
+
extends IEnsureControllerDataRootOptions {
|
|
56
|
+
retainOnFailure: boolean;
|
|
57
|
+
onBlocked?: (errorArg: Error) => Promise<void>;
|
|
58
|
+
retryDelayMs?: number;
|
|
59
|
+
ensureDataRoot?: typeof ensureControllerDataRoot;
|
|
60
|
+
reportBlocked?: (errorArg: Error) => void;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const ensureControllerDataRootForStartup = async (
|
|
64
|
+
optionsArg: IEnsureControllerDataRootForStartupOptions,
|
|
65
|
+
): Promise<IControllerDataRootMigrationResult> => {
|
|
66
|
+
const ensureDataRoot = optionsArg.ensureDataRoot ?? ensureControllerDataRoot;
|
|
67
|
+
let lastFailureLogAt = 0;
|
|
68
|
+
while (true) {
|
|
69
|
+
try {
|
|
70
|
+
return await ensureDataRoot(optionsArg);
|
|
71
|
+
} catch (errorArg) {
|
|
72
|
+
if (!optionsArg.retainOnFailure) throw errorArg;
|
|
73
|
+
const error = errorArg instanceof Error ? errorArg : new Error(String(errorArg));
|
|
74
|
+
if (Date.now() - lastFailureLogAt >= 30_000) {
|
|
75
|
+
lastFailureLogAt = Date.now();
|
|
76
|
+
try {
|
|
77
|
+
(optionsArg.reportBlocked ?? ((reportedErrorArg) => console.error(
|
|
78
|
+
'Controller data-root migration is blocked; startup remains fail-closed.',
|
|
79
|
+
reportedErrorArg,
|
|
80
|
+
)))(error);
|
|
81
|
+
} catch (reportErrorArg) {
|
|
82
|
+
console.error('Reporting the blocked controller startup failed.', reportErrorArg);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
await optionsArg.onBlocked?.(error);
|
|
87
|
+
} catch (reportErrorArg) {
|
|
88
|
+
console.error('Publishing the blocked controller startup state failed.', reportErrorArg);
|
|
89
|
+
}
|
|
90
|
+
await new Promise<void>((resolve, reject) => {
|
|
91
|
+
const signal = optionsArg.signal;
|
|
92
|
+
const timeout = setTimeout(finish, optionsArg.retryDelayMs ?? 5_000);
|
|
93
|
+
const onAbort = () => finish(signal?.reason instanceof Error
|
|
94
|
+
? signal.reason
|
|
95
|
+
: new Error('Controller startup was cancelled by shutdown.'));
|
|
96
|
+
function finish(errorArg?: Error): void {
|
|
97
|
+
clearTimeout(timeout);
|
|
98
|
+
signal?.removeEventListener('abort', onAbort);
|
|
99
|
+
if (errorArg) reject(errorArg);
|
|
100
|
+
else resolve();
|
|
101
|
+
}
|
|
102
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
103
|
+
if (signal?.aborted) onAbort();
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
|
|
3
|
+
export const embeddedDatabaseSocketPath = (dataDirectoryArg: string): string => {
|
|
4
|
+
const directoryHash = plugins.crypto
|
|
5
|
+
.createHash('sha256')
|
|
6
|
+
.update(dataDirectoryArg)
|
|
7
|
+
.digest('hex')
|
|
8
|
+
.slice(0, 16);
|
|
9
|
+
return plugins.path.join(plugins.os.tmpdir(), `harness-controller-${directoryHash}.sock`);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const isSocketListening = (socketPathArg: string): Promise<boolean> => new Promise((resolve) => {
|
|
13
|
+
const probe = plugins.net.createConnection({ path: socketPathArg }, () => {
|
|
14
|
+
probe.destroy();
|
|
15
|
+
resolve(true);
|
|
16
|
+
});
|
|
17
|
+
probe.on('error', () => resolve(false));
|
|
18
|
+
probe.setTimeout(500, () => {
|
|
19
|
+
probe.destroy();
|
|
20
|
+
resolve(false);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
|
|
3
|
+
export interface IFlexCredentialRuntimeStore {
|
|
4
|
+
getEntry(accountArg: string): Promise<Uint8Array | null>;
|
|
5
|
+
setEntry(accountArg: string, valueArg: Uint8Array): Promise<void>;
|
|
6
|
+
deleteEntry(accountArg: string): Promise<boolean>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface IFlexCredentialRuntimeStorePair<
|
|
10
|
+
TKernelStore extends IFlexCredentialRuntimeStore,
|
|
11
|
+
TSealedStore extends IFlexCredentialRuntimeStore,
|
|
12
|
+
> {
|
|
13
|
+
kernelStore: TKernelStore;
|
|
14
|
+
sealedStore: TSealedStore;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface IFlexCredentialRuntimeOperationOptions<
|
|
18
|
+
TKernelStore extends IFlexCredentialRuntimeStore,
|
|
19
|
+
TSealedStore extends IFlexCredentialRuntimeStore,
|
|
20
|
+
> {
|
|
21
|
+
stores: IFlexCredentialRuntimeStorePair<TKernelStore, TSealedStore>;
|
|
22
|
+
recreateStores: (
|
|
23
|
+
storesArg: IFlexCredentialRuntimeStorePair<TKernelStore, TSealedStore>,
|
|
24
|
+
) => Promise<IFlexCredentialRuntimeStorePair<TKernelStore, TSealedStore>>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const maximumMutationAttempts = 2;
|
|
28
|
+
|
|
29
|
+
const isMutationOutcomeUnknown = (errorArg: unknown): boolean => (
|
|
30
|
+
errorArg instanceof plugins.smartsecret.SmartSecretKernelStoreError
|
|
31
|
+
|| errorArg instanceof plugins.smartsecret.SmartSecretSealedFileStoreError
|
|
32
|
+
) && errorArg.code === 'MUTATION_OUTCOME_UNKNOWN';
|
|
33
|
+
|
|
34
|
+
const bytesEqual = (leftArg: Uint8Array, rightArg: Uint8Array): boolean =>
|
|
35
|
+
leftArg.byteLength === rightArg.byteLength
|
|
36
|
+
&& plugins.crypto.timingSafeEqual(leftArg, rightArg);
|
|
37
|
+
|
|
38
|
+
const reconciliationError = (): Error =>
|
|
39
|
+
new Error('Flex provider credential mutation reconciliation failed.');
|
|
40
|
+
|
|
41
|
+
export const writeFlexProviderCredentialEntry = async <
|
|
42
|
+
TKernelStore extends IFlexCredentialRuntimeStore,
|
|
43
|
+
TSealedStore extends IFlexCredentialRuntimeStore,
|
|
44
|
+
>(
|
|
45
|
+
optionsArg: IFlexCredentialRuntimeOperationOptions<TKernelStore, TSealedStore>,
|
|
46
|
+
accountArg: string,
|
|
47
|
+
bytesArg: Uint8Array,
|
|
48
|
+
): Promise<IFlexCredentialRuntimeStorePair<TKernelStore, TSealedStore>> => {
|
|
49
|
+
let stores = optionsArg.stores;
|
|
50
|
+
let previousBytes = await stores.sealedStore.getEntry(accountArg);
|
|
51
|
+
let requiresConfirmedRetry = false;
|
|
52
|
+
try {
|
|
53
|
+
for (let attempt = 0; attempt < maximumMutationAttempts; attempt += 1) {
|
|
54
|
+
let outcomeWasUnknown = false;
|
|
55
|
+
try {
|
|
56
|
+
await stores.sealedStore.setEntry(accountArg, bytesArg);
|
|
57
|
+
} catch (errorArg) {
|
|
58
|
+
if (!isMutationOutcomeUnknown(errorArg)) throw errorArg;
|
|
59
|
+
stores = await optionsArg.recreateStores(stores);
|
|
60
|
+
outcomeWasUnknown = true;
|
|
61
|
+
requiresConfirmedRetry = true;
|
|
62
|
+
}
|
|
63
|
+
const readback = await stores.sealedStore.getEntry(accountArg);
|
|
64
|
+
try {
|
|
65
|
+
if (readback && bytesEqual(readback, bytesArg)) {
|
|
66
|
+
if (!requiresConfirmedRetry || !outcomeWasUnknown) return stores;
|
|
67
|
+
previousBytes?.fill(0);
|
|
68
|
+
previousBytes = readback;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const unchanged = previousBytes
|
|
72
|
+
? Boolean(readback && bytesEqual(readback, previousBytes))
|
|
73
|
+
: readback === null;
|
|
74
|
+
if (!outcomeWasUnknown || !unchanged || attempt === maximumMutationAttempts - 1) {
|
|
75
|
+
throw reconciliationError();
|
|
76
|
+
}
|
|
77
|
+
previousBytes?.fill(0);
|
|
78
|
+
previousBytes = readback;
|
|
79
|
+
} finally {
|
|
80
|
+
if (readback !== previousBytes) readback?.fill(0);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
throw reconciliationError();
|
|
84
|
+
} finally {
|
|
85
|
+
previousBytes?.fill(0);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const deleteFlexProviderCredentialEntry = async <
|
|
90
|
+
TKernelStore extends IFlexCredentialRuntimeStore,
|
|
91
|
+
TSealedStore extends IFlexCredentialRuntimeStore,
|
|
92
|
+
>(
|
|
93
|
+
optionsArg: IFlexCredentialRuntimeOperationOptions<TKernelStore, TSealedStore>,
|
|
94
|
+
accountArg: string,
|
|
95
|
+
): Promise<IFlexCredentialRuntimeStorePair<TKernelStore, TSealedStore>> => {
|
|
96
|
+
let stores = optionsArg.stores;
|
|
97
|
+
const previousBytes = await stores.sealedStore.getEntry(accountArg);
|
|
98
|
+
let requiresConfirmedRetry = false;
|
|
99
|
+
try {
|
|
100
|
+
for (let attempt = 0; attempt < maximumMutationAttempts; attempt += 1) {
|
|
101
|
+
let outcomeWasUnknown = false;
|
|
102
|
+
try {
|
|
103
|
+
await stores.sealedStore.deleteEntry(accountArg);
|
|
104
|
+
} catch (errorArg) {
|
|
105
|
+
if (!isMutationOutcomeUnknown(errorArg)) throw errorArg;
|
|
106
|
+
stores = await optionsArg.recreateStores(stores);
|
|
107
|
+
outcomeWasUnknown = true;
|
|
108
|
+
requiresConfirmedRetry = true;
|
|
109
|
+
}
|
|
110
|
+
const readback = await stores.sealedStore.getEntry(accountArg);
|
|
111
|
+
try {
|
|
112
|
+
if (!readback) {
|
|
113
|
+
if (!requiresConfirmedRetry || !outcomeWasUnknown) return stores;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const unchanged = Boolean(previousBytes && bytesEqual(readback, previousBytes));
|
|
117
|
+
if (!outcomeWasUnknown || !unchanged || attempt === maximumMutationAttempts - 1) {
|
|
118
|
+
throw reconciliationError();
|
|
119
|
+
}
|
|
120
|
+
} finally {
|
|
121
|
+
readback?.fill(0);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
throw reconciliationError();
|
|
125
|
+
} finally {
|
|
126
|
+
previousBytes?.fill(0);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import type { IFlexOpenCodeOAuthAuth } from './interfaces.flexipc.js';
|
|
3
|
+
|
|
4
|
+
const parseAccessTokenExpiry = (accessTokenArg: string): number => {
|
|
5
|
+
const payload = accessTokenArg.split('.')[1];
|
|
6
|
+
if (!payload || Buffer.byteLength(payload, 'utf8') > 32 * 1024) return 0;
|
|
7
|
+
try {
|
|
8
|
+
const claims = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8')) as unknown;
|
|
9
|
+
if (
|
|
10
|
+
!claims
|
|
11
|
+
|| typeof claims !== 'object'
|
|
12
|
+
|| Array.isArray(claims)
|
|
13
|
+
|| !Number.isSafeInteger((claims as { exp?: unknown }).exp)
|
|
14
|
+
|| Number((claims as { exp: number }).exp) < 0
|
|
15
|
+
) return 0;
|
|
16
|
+
const expires = Number((claims as { exp: number }).exp) * 1_000;
|
|
17
|
+
return Number.isSafeInteger(expires) ? expires : 0;
|
|
18
|
+
} catch {
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const openCodeOAuthAuthFromProviderCredential = (
|
|
24
|
+
credentialArg: plugins.smartaiProviders.IOpenAiChatGptOAuthCredential,
|
|
25
|
+
): IFlexOpenCodeOAuthAuth => {
|
|
26
|
+
const auth = plugins.smartaiProviders.toOpenAiChatGptAuthCredentials(credentialArg);
|
|
27
|
+
return {
|
|
28
|
+
type: 'oauth',
|
|
29
|
+
access: credentialArg.accessToken,
|
|
30
|
+
refresh: credentialArg.refreshToken,
|
|
31
|
+
expires: parseAccessTokenExpiry(credentialArg.accessToken),
|
|
32
|
+
...(auth.accountId ? { accountId: auth.accountId } : {}),
|
|
33
|
+
};
|
|
34
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const controllerEnvironmentPrefix = 'HARNESS_CONTROLLER_';
|
|
2
|
+
const forwardedEnvironmentVariable = 'HARNESS_CONTROLLER_FORWARD_ENV';
|
|
3
|
+
const runtimeEnvironmentAllowlist = new Set([
|
|
4
|
+
'APPDATA', 'COLORTERM', 'COMSPEC', 'FORCE_COLOR', 'HOME', 'HOMEDRIVE',
|
|
5
|
+
'HOMEPATH', 'HTTP_PROXY', 'HTTPS_PROXY', 'LANG', 'LOCALAPPDATA', 'LOGNAME',
|
|
6
|
+
'NODE_EXTRA_CA_CERTS', 'NO_PROXY', 'PATH', 'PATHEXT', 'SHELL', 'SSL_CERT_DIR',
|
|
7
|
+
'SSL_CERT_FILE', 'SYSTEMROOT', 'TEMP', 'TERM', 'TMP', 'TMPDIR', 'TZ', 'USER',
|
|
8
|
+
'USERPROFILE', 'WINDIR', 'XDG_CACHE_HOME', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME',
|
|
9
|
+
'XDG_RUNTIME_DIR', 'XDG_STATE_HOME',
|
|
10
|
+
]);
|
|
11
|
+
const environmentVariablePattern = /^[A-Za-z_][A-Za-z0-9_]*$/u;
|
|
12
|
+
const proxyEnvironmentVariables = new Set(['HTTP_PROXY', 'HTTPS_PROXY']);
|
|
13
|
+
const maxForwardedEnvironmentVariables = 64;
|
|
14
|
+
const maxForwardingDirectiveBytes = 4096;
|
|
15
|
+
|
|
16
|
+
const readExplicitlyForwardedEnvironmentNames = (environmentArg: NodeJS.ProcessEnv): string[] => {
|
|
17
|
+
const forwardingDirective = environmentArg[forwardedEnvironmentVariable];
|
|
18
|
+
if (!forwardingDirective) return [];
|
|
19
|
+
if (Buffer.byteLength(forwardingDirective, 'utf8') > maxForwardingDirectiveBytes) {
|
|
20
|
+
throw new Error(`${forwardedEnvironmentVariable} exceeds its size limit.`);
|
|
21
|
+
}
|
|
22
|
+
const requestedNames = forwardingDirective
|
|
23
|
+
.split(',')
|
|
24
|
+
.map((name) => name.trim())
|
|
25
|
+
.filter((name) => name.length > 0);
|
|
26
|
+
if (requestedNames.length > maxForwardedEnvironmentVariables) {
|
|
27
|
+
throw new Error(`${forwardedEnvironmentVariable} names too many variables.`);
|
|
28
|
+
}
|
|
29
|
+
const uniqueNames = [...new Set(requestedNames)];
|
|
30
|
+
for (const name of uniqueNames) {
|
|
31
|
+
if (!environmentVariablePattern.test(name)) {
|
|
32
|
+
throw new Error(`${forwardedEnvironmentVariable} contains an invalid variable name.`);
|
|
33
|
+
}
|
|
34
|
+
const normalizedName = name.toUpperCase();
|
|
35
|
+
if (
|
|
36
|
+
normalizedName === 'HARNESS_CONTROLLER'
|
|
37
|
+
|| normalizedName.startsWith(controllerEnvironmentPrefix)
|
|
38
|
+
|| normalizedName === 'OPENCODE_SERVER_USERNAME'
|
|
39
|
+
|| normalizedName === 'OPENCODE_SERVER_PASSWORD'
|
|
40
|
+
|| normalizedName === 'OPENCODE_AUTH_CONTENT'
|
|
41
|
+
) {
|
|
42
|
+
throw new Error(`${forwardedEnvironmentVariable} cannot forward ${name}.`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return uniqueNames;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const proxyValueRequiresExplicitForwarding = (valueArg: string): boolean => {
|
|
49
|
+
try {
|
|
50
|
+
const parsed = new URL(valueArg);
|
|
51
|
+
return parsed.username.length > 0 || parsed.password.length > 0;
|
|
52
|
+
} catch {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/** Environment safe to expose to controller-owned child runtimes and terminals. */
|
|
58
|
+
export const createSanitizedRuntimeEnvironment = (
|
|
59
|
+
environmentArg: NodeJS.ProcessEnv = process.env,
|
|
60
|
+
): NodeJS.ProcessEnv => {
|
|
61
|
+
if (Object.entries(environmentArg).some(([key, value]) => (
|
|
62
|
+
value !== undefined && key.toUpperCase() === 'OPENCODE_AUTH_CONTENT'
|
|
63
|
+
))) {
|
|
64
|
+
throw new Error('OPENCODE_AUTH_CONTENT cannot be supplied to controller-owned runtimes.');
|
|
65
|
+
}
|
|
66
|
+
const environment: NodeJS.ProcessEnv = {};
|
|
67
|
+
const explicitlyForwardedNames = readExplicitlyForwardedEnvironmentNames(environmentArg);
|
|
68
|
+
const explicitlyForwardedNormalizedNames = new Set(
|
|
69
|
+
explicitlyForwardedNames.map((nameArg) => nameArg.toUpperCase()),
|
|
70
|
+
);
|
|
71
|
+
for (const [key, value] of Object.entries(environmentArg)) {
|
|
72
|
+
const normalizedKey = key.toUpperCase();
|
|
73
|
+
if (
|
|
74
|
+
value !== undefined
|
|
75
|
+
&& (runtimeEnvironmentAllowlist.has(normalizedKey) || normalizedKey.startsWith('LC_'))
|
|
76
|
+
&& (
|
|
77
|
+
!proxyEnvironmentVariables.has(normalizedKey)
|
|
78
|
+
|| explicitlyForwardedNormalizedNames.has(normalizedKey)
|
|
79
|
+
|| !proxyValueRequiresExplicitForwarding(value)
|
|
80
|
+
)
|
|
81
|
+
) {
|
|
82
|
+
environment[key] = value;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
for (const name of explicitlyForwardedNames) {
|
|
86
|
+
const value = environmentArg[name];
|
|
87
|
+
if (value !== undefined) environment[name] = value;
|
|
88
|
+
}
|
|
89
|
+
return environment;
|
|
90
|
+
};
|