@c0sc0s/orbit 0.5.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/README.md +38 -0
- package/assets/README.md +9 -0
- package/assets/app-icon.png +0 -0
- package/assets/icon.icns +0 -0
- package/assets/logo.png +0 -0
- package/dist/cdp/client.d.ts +33 -0
- package/dist/cdp/client.d.ts.map +1 -0
- package/dist/cdp/client.js +159 -0
- package/dist/cdp/client.js.map +1 -0
- package/dist/cdp/expressions.d.ts +8 -0
- package/dist/cdp/expressions.d.ts.map +1 -0
- package/dist/cdp/expressions.js +27 -0
- package/dist/cdp/expressions.js.map +1 -0
- package/dist/cdp/target-registry.d.ts +33 -0
- package/dist/cdp/target-registry.d.ts.map +1 -0
- package/dist/cdp/target-registry.js +139 -0
- package/dist/cdp/target-registry.js.map +1 -0
- package/dist/cli/arguments.d.ts +13 -0
- package/dist/cli/arguments.d.ts.map +1 -0
- package/dist/cli/arguments.js +29 -0
- package/dist/cli/arguments.js.map +1 -0
- package/dist/cli/platform.d.ts +2 -0
- package/dist/cli/platform.d.ts.map +1 -0
- package/dist/cli/platform.js +68 -0
- package/dist/cli/platform.js.map +1 -0
- package/dist/cli/run.d.ts +2 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +148 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +13 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/manifest.d.ts +12 -0
- package/dist/config/manifest.d.ts.map +1 -0
- package/dist/config/manifest.js +71 -0
- package/dist/config/manifest.js.map +1 -0
- package/dist/host/daemon.d.ts +54 -0
- package/dist/host/daemon.d.ts.map +1 -0
- package/dist/host/daemon.js +185 -0
- package/dist/host/daemon.js.map +1 -0
- package/dist/host/plugin-host.d.ts +50 -0
- package/dist/host/plugin-host.d.ts.map +1 -0
- package/dist/host/plugin-host.js +230 -0
- package/dist/host/plugin-host.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/installation/entry.d.ts +2 -0
- package/dist/installation/entry.d.ts.map +1 -0
- package/dist/installation/entry.js +14 -0
- package/dist/installation/entry.js.map +1 -0
- package/dist/installation/files.d.ts +5 -0
- package/dist/installation/files.d.ts.map +1 -0
- package/dist/installation/files.js +101 -0
- package/dist/installation/files.js.map +1 -0
- package/dist/installation/legacy.d.ts +19 -0
- package/dist/installation/legacy.d.ts.map +1 -0
- package/dist/installation/legacy.js +52 -0
- package/dist/installation/legacy.js.map +1 -0
- package/dist/installation/lock.d.ts +2 -0
- package/dist/installation/lock.d.ts.map +1 -0
- package/dist/installation/lock.js +18 -0
- package/dist/installation/lock.js.map +1 -0
- package/dist/installation/manager.d.ts +180 -0
- package/dist/installation/manager.d.ts.map +1 -0
- package/dist/installation/manager.js +469 -0
- package/dist/installation/manager.js.map +1 -0
- package/dist/lifecycle/resource-scope.d.ts +11 -0
- package/dist/lifecycle/resource-scope.d.ts.map +1 -0
- package/dist/lifecycle/resource-scope.js +40 -0
- package/dist/lifecycle/resource-scope.js.map +1 -0
- package/dist/platform/codex-process.d.ts +33 -0
- package/dist/platform/codex-process.d.ts.map +1 -0
- package/dist/platform/codex-process.js +122 -0
- package/dist/platform/codex-process.js.map +1 -0
- package/dist/platform/launcher.d.ts +12 -0
- package/dist/platform/launcher.d.ts.map +1 -0
- package/dist/platform/launcher.js +46 -0
- package/dist/platform/launcher.js.map +1 -0
- package/dist/plugins/codex-extension.d.ts +12 -0
- package/dist/plugins/codex-extension.d.ts.map +1 -0
- package/dist/plugins/codex-extension.js +100 -0
- package/dist/plugins/codex-extension.js.map +1 -0
- package/dist/plugins/manifest.d.ts +20 -0
- package/dist/plugins/manifest.d.ts.map +1 -0
- package/dist/plugins/manifest.js +51 -0
- package/dist/plugins/manifest.js.map +1 -0
- package/dist/plugins/registry.d.ts +3 -0
- package/dist/plugins/registry.d.ts.map +1 -0
- package/dist/plugins/registry.js +26 -0
- package/dist/plugins/registry.js.map +1 -0
- package/dist/protocol/ipc.d.ts +38 -0
- package/dist/protocol/ipc.d.ts.map +1 -0
- package/dist/protocol/ipc.js +5 -0
- package/dist/protocol/ipc.js.map +1 -0
- package/dist/protocol/types.d.ts +55 -0
- package/dist/protocol/types.d.ts.map +1 -0
- package/dist/protocol/types.js +2 -0
- package/dist/protocol/types.js.map +1 -0
- package/dist/renderer/loader.d.ts +32 -0
- package/dist/renderer/loader.d.ts.map +1 -0
- package/dist/renderer/loader.js +130 -0
- package/dist/renderer/loader.js.map +1 -0
- package/dist/renderer/transport.d.ts +28 -0
- package/dist/renderer/transport.d.ts.map +1 -0
- package/dist/renderer/transport.js +89 -0
- package/dist/renderer/transport.js.map +1 -0
- package/dist/sdk/contracts.d.ts +50 -0
- package/dist/sdk/contracts.d.ts.map +1 -0
- package/dist/sdk/contracts.js +2 -0
- package/dist/sdk/contracts.js.map +1 -0
- package/dist/service/module.d.ts +24 -0
- package/dist/service/module.d.ts.map +1 -0
- package/dist/service/module.js +97 -0
- package/dist/service/module.js.map +1 -0
- package/dist/service/process.d.ts +32 -0
- package/dist/service/process.d.ts.map +1 -0
- package/dist/service/process.js +95 -0
- package/dist/service/process.js.map +1 -0
- package/dist/service/worker.d.ts +2 -0
- package/dist/service/worker.d.ts.map +1 -0
- package/dist/service/worker.js +116 -0
- package/dist/service/worker.js.map +1 -0
- package/examples/hello.js +10 -0
- package/examples/loader.json +4 -0
- package/examples/service-loader.json +4 -0
- package/examples/service.js +10 -0
- package/examples/service.mjs +3 -0
- package/package.json +40 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { daemonStatus } from "../host/daemon.js";
|
|
2
|
+
import type { PluginManifest } from "../plugins/manifest.js";
|
|
3
|
+
import type { PluginEntry } from "../sdk/contracts.js";
|
|
4
|
+
import type { ExecFileOptions } from "node:child_process";
|
|
5
|
+
export interface InstalledPlugin {
|
|
6
|
+
manifest: PluginManifest;
|
|
7
|
+
path: string;
|
|
8
|
+
packageName: string;
|
|
9
|
+
dataDirectory: string;
|
|
10
|
+
externalData: boolean;
|
|
11
|
+
}
|
|
12
|
+
interface RuntimeVersion {
|
|
13
|
+
version: string;
|
|
14
|
+
path: string;
|
|
15
|
+
}
|
|
16
|
+
export interface PlatformConfig {
|
|
17
|
+
apiVersion: 1;
|
|
18
|
+
plugins: PluginEntry[];
|
|
19
|
+
orbit: {
|
|
20
|
+
schemaVersion: 1;
|
|
21
|
+
runtime: RuntimeVersion;
|
|
22
|
+
previousRuntime?: RuntimeVersion;
|
|
23
|
+
packages: Record<string, InstalledPlugin>;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export interface InstallationOptions {
|
|
27
|
+
codexBinary?: string;
|
|
28
|
+
manageExtensions?: boolean;
|
|
29
|
+
home?: string;
|
|
30
|
+
root?: string;
|
|
31
|
+
packageRoot?: string;
|
|
32
|
+
applicationsRoot?: string;
|
|
33
|
+
platform?: string;
|
|
34
|
+
port?: number;
|
|
35
|
+
getDaemonStatus?: typeof daemonStatus;
|
|
36
|
+
run?: (file: string, args: string[], options: ExecFileOptions) => Promise<{
|
|
37
|
+
stdout: string;
|
|
38
|
+
stderr: string;
|
|
39
|
+
}>;
|
|
40
|
+
}
|
|
41
|
+
export declare function resolvePluginId(config: PlatformConfig, id: string): string;
|
|
42
|
+
export declare function createInstallationManager(options?: InstallationOptions): {
|
|
43
|
+
paths: {
|
|
44
|
+
root: string;
|
|
45
|
+
configPath: string;
|
|
46
|
+
launcherPath: string;
|
|
47
|
+
};
|
|
48
|
+
install: () => Promise<{
|
|
49
|
+
status: string;
|
|
50
|
+
root: string;
|
|
51
|
+
launcherPath: string;
|
|
52
|
+
version?: undefined;
|
|
53
|
+
} | {
|
|
54
|
+
status: string;
|
|
55
|
+
root: string;
|
|
56
|
+
launcherPath: string;
|
|
57
|
+
version: string;
|
|
58
|
+
}>;
|
|
59
|
+
update: (candidate?: string) => Promise<{
|
|
60
|
+
status: string;
|
|
61
|
+
version: string;
|
|
62
|
+
}>;
|
|
63
|
+
updatePackage: () => Promise<{
|
|
64
|
+
status: string;
|
|
65
|
+
version: string;
|
|
66
|
+
}>;
|
|
67
|
+
rollback: () => Promise<{
|
|
68
|
+
status: string;
|
|
69
|
+
version: string;
|
|
70
|
+
}>;
|
|
71
|
+
migrateLegacy: (directory: string) => Promise<{
|
|
72
|
+
migrated: boolean;
|
|
73
|
+
dataPreserved?: undefined;
|
|
74
|
+
} | {
|
|
75
|
+
migrated: boolean;
|
|
76
|
+
dataPreserved: string;
|
|
77
|
+
}>;
|
|
78
|
+
addPlugin: (candidate: string, settings?: {
|
|
79
|
+
dataDirectory?: string;
|
|
80
|
+
}) => Promise<{
|
|
81
|
+
status: string;
|
|
82
|
+
id: string;
|
|
83
|
+
version: string;
|
|
84
|
+
path: string;
|
|
85
|
+
}>;
|
|
86
|
+
setEnabled: (id: string, enabled: boolean) => Promise<{
|
|
87
|
+
status: string;
|
|
88
|
+
id: string;
|
|
89
|
+
}>;
|
|
90
|
+
uninstallPlugin: (id: string, purge?: boolean) => Promise<{
|
|
91
|
+
status: string;
|
|
92
|
+
id: string;
|
|
93
|
+
settingsPreserved: boolean;
|
|
94
|
+
}>;
|
|
95
|
+
installPackage: (spec: string, expectedId?: string) => Promise<{
|
|
96
|
+
status: string;
|
|
97
|
+
id: string;
|
|
98
|
+
version: string;
|
|
99
|
+
path: string;
|
|
100
|
+
}>;
|
|
101
|
+
start: (attach?: boolean) => Promise<{
|
|
102
|
+
stdout: string;
|
|
103
|
+
stderr: string;
|
|
104
|
+
}>;
|
|
105
|
+
status: () => Promise<{
|
|
106
|
+
installed: boolean;
|
|
107
|
+
root: string;
|
|
108
|
+
runtime?: undefined;
|
|
109
|
+
daemon?: undefined;
|
|
110
|
+
plugins?: undefined;
|
|
111
|
+
packages?: undefined;
|
|
112
|
+
} | {
|
|
113
|
+
installed: boolean;
|
|
114
|
+
root: string;
|
|
115
|
+
runtime: RuntimeVersion;
|
|
116
|
+
daemon: {
|
|
117
|
+
running: boolean;
|
|
118
|
+
pid: null;
|
|
119
|
+
conflict?: undefined;
|
|
120
|
+
phase?: undefined;
|
|
121
|
+
} | {
|
|
122
|
+
running: boolean;
|
|
123
|
+
pid: number | null;
|
|
124
|
+
conflict: boolean;
|
|
125
|
+
phase: string;
|
|
126
|
+
};
|
|
127
|
+
plugins: PluginEntry[];
|
|
128
|
+
packages: Record<string, InstalledPlugin>;
|
|
129
|
+
}>;
|
|
130
|
+
doctor: () => Promise<{
|
|
131
|
+
ok: boolean;
|
|
132
|
+
checks: {
|
|
133
|
+
id: string;
|
|
134
|
+
ok: boolean;
|
|
135
|
+
}[];
|
|
136
|
+
status: {
|
|
137
|
+
installed: boolean;
|
|
138
|
+
root: string;
|
|
139
|
+
runtime?: undefined;
|
|
140
|
+
daemon?: undefined;
|
|
141
|
+
plugins?: undefined;
|
|
142
|
+
packages?: undefined;
|
|
143
|
+
} | {
|
|
144
|
+
installed: boolean;
|
|
145
|
+
root: string;
|
|
146
|
+
runtime: RuntimeVersion;
|
|
147
|
+
daemon: {
|
|
148
|
+
running: boolean;
|
|
149
|
+
pid: null;
|
|
150
|
+
conflict?: undefined;
|
|
151
|
+
phase?: undefined;
|
|
152
|
+
} | {
|
|
153
|
+
running: boolean;
|
|
154
|
+
pid: number | null;
|
|
155
|
+
conflict: boolean;
|
|
156
|
+
phase: string;
|
|
157
|
+
};
|
|
158
|
+
plugins: PluginEntry[];
|
|
159
|
+
packages: Record<string, InstalledPlugin>;
|
|
160
|
+
};
|
|
161
|
+
}>;
|
|
162
|
+
extensionStatus: (id: string) => Promise<{
|
|
163
|
+
installed: boolean;
|
|
164
|
+
enabled: boolean;
|
|
165
|
+
payloadPresent: boolean;
|
|
166
|
+
error?: undefined;
|
|
167
|
+
} | {
|
|
168
|
+
installed: boolean;
|
|
169
|
+
enabled: boolean;
|
|
170
|
+
payloadPresent: boolean;
|
|
171
|
+
error: string;
|
|
172
|
+
}>;
|
|
173
|
+
read: () => Promise<PlatformConfig>;
|
|
174
|
+
run: (file: string, args: string[], settings?: ExecFileOptions) => Promise<{
|
|
175
|
+
stdout: string;
|
|
176
|
+
stderr: string;
|
|
177
|
+
}>;
|
|
178
|
+
};
|
|
179
|
+
export {};
|
|
180
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/installation/manager.ts"],"names":[],"mappings":"AAYA,OAAO,EAAe,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAI9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1D,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,cAAc,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;CACvB;AACD,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AACD,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,CAAC,CAAC;IACd,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,KAAK,EAAE;QACL,aAAa,EAAE,CAAC,CAAC;QACjB,OAAO,EAAE,cAAc,CAAC;QACxB,eAAe,CAAC,EAAE,cAAc,CAAC;QACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;KAC3C,CAAC;CACH;AACD,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,OAAO,YAAY,CAAC;IACtC,GAAG,CAAC,EAAE,CACJ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,eAAe,KACrB,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD;AAID,wBAAgB,eAAe,CAAC,MAAM,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAK1E;AAOD,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,mBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAsVjC,MAAM;;;;;;;2BAtJV,MAAM,aAAY;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE;;;;;;qBAoGlD,MAAM,WAAW,OAAO;;;;0BASnB,MAAM;;;;;2BA8BL,MAAM,eAAe,MAAM;;;;;;;gBA5VxC,MAAM;gBAAU,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAuYV,MAAM;;;;;;;;;;;gBArVlB,OAAO,CAAC,cAAc,CAAC;gBAxB3B,MAAM,QAAQ,MAAM,EAAE,aAAY,eAAe;;;;EAkcrE"}
|
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
import { existsSync, lstatSync, realpathSync } from "node:fs";
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { cp, lstat, mkdir, readFile, realpath, rm } from "node:fs/promises";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { promisify } from "node:util";
|
|
8
|
+
import { randomUUID } from "node:crypto";
|
|
9
|
+
import { withRegistryPackage } from "../plugins/registry.js";
|
|
10
|
+
import { retireLegacyTags } from "./legacy.js";
|
|
11
|
+
import { createCodexExtensionManager } from "../plugins/codex-extension.js";
|
|
12
|
+
import { createLauncher } from "../platform/launcher.js";
|
|
13
|
+
import { daemonPaths, daemonStatus } from "../host/daemon.js";
|
|
14
|
+
import { assertCompatible, readPluginManifest } from "../plugins/manifest.js";
|
|
15
|
+
import { atomicJson, exists, inside, snapshotPackage } from "./files.js";
|
|
16
|
+
import { withInstallationLock } from "./lock.js";
|
|
17
|
+
const execute = promisify(execFile);
|
|
18
|
+
const packageRoot = fileURLToPath(new URL("../../", import.meta.url));
|
|
19
|
+
export function resolvePluginId(config, id) {
|
|
20
|
+
if (Object.hasOwn(config.orbit.packages, id))
|
|
21
|
+
return id;
|
|
22
|
+
const matches = Object.values(config.orbit.packages).filter(plugin => plugin.manifest.legacyIds?.includes(id));
|
|
23
|
+
if (matches.length > 1)
|
|
24
|
+
throw new Error(`Ambiguous plugin alias ${id}`);
|
|
25
|
+
return matches[0]?.manifest.id ?? id;
|
|
26
|
+
}
|
|
27
|
+
function canonicalPath(path) {
|
|
28
|
+
path = resolve(path);
|
|
29
|
+
if (existsSync(path))
|
|
30
|
+
return lstatSync(path).isSymbolicLink() ? path : realpathSync(path);
|
|
31
|
+
return join(canonicalPath(dirname(path)), basename(path));
|
|
32
|
+
}
|
|
33
|
+
export function createInstallationManager(options = {}) {
|
|
34
|
+
const home = options.home ?? homedir();
|
|
35
|
+
const root = canonicalPath(options.root ?? process.env.ORBIT_HOME ?? join(home, "Library/Application Support/Orbit"));
|
|
36
|
+
const source = canonicalPath(options.packageRoot ?? packageRoot);
|
|
37
|
+
const configPath = join(root, "loader.json");
|
|
38
|
+
const launcherPath = join(options.applicationsRoot ?? join(home, "Applications"), "Orbit.app");
|
|
39
|
+
const port = options.port ?? Number(process.env.ORBIT_CDP_PORT ?? 9341);
|
|
40
|
+
const run = (file, args, settings = {}) => (options.run ?? execute)(file, args, {
|
|
41
|
+
timeout: 90_000,
|
|
42
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
43
|
+
...settings,
|
|
44
|
+
});
|
|
45
|
+
for (const protectedPath of [
|
|
46
|
+
home,
|
|
47
|
+
...(["runtime", "plugins", "marketplaces"].some((folder) => source.startsWith(`${root}/${folder}/`))
|
|
48
|
+
? []
|
|
49
|
+
: [source]),
|
|
50
|
+
process.env.CODEX_HOME ?? join(home, ".codex"),
|
|
51
|
+
].map((path) => canonicalPath(path))) {
|
|
52
|
+
if (protectedPath === root || protectedPath.startsWith(`${root}/`) || root === "/")
|
|
53
|
+
throw new Error("Unsafe Orbit installation directory");
|
|
54
|
+
}
|
|
55
|
+
const extensions = createCodexExtensionManager(root, run, options.codexBinary);
|
|
56
|
+
async function prepare() {
|
|
57
|
+
await mkdir(root, { recursive: true, mode: 0o700 });
|
|
58
|
+
if ((await lstat(root)).isSymbolicLink())
|
|
59
|
+
throw new Error("Orbit root must not traverse a symlink");
|
|
60
|
+
}
|
|
61
|
+
async function read() {
|
|
62
|
+
if (!(await exists(configPath)))
|
|
63
|
+
throw new Error("Orbit is not installed; run orbit install first");
|
|
64
|
+
const config = JSON.parse(await readFile(configPath, "utf8"));
|
|
65
|
+
if (config.apiVersion !== 1 ||
|
|
66
|
+
config.orbit?.schemaVersion !== 1 ||
|
|
67
|
+
!Array.isArray(config.plugins) ||
|
|
68
|
+
!config.orbit.packages)
|
|
69
|
+
throw new Error("Not an Orbit-owned installation");
|
|
70
|
+
inside(root, config.orbit.runtime.path);
|
|
71
|
+
return config;
|
|
72
|
+
}
|
|
73
|
+
async function runtimeCommand(config, command, attach = false) {
|
|
74
|
+
const cli = join(inside(root, config.orbit.runtime.path), "dist/cli.js");
|
|
75
|
+
return run(process.execPath, [
|
|
76
|
+
cli,
|
|
77
|
+
command,
|
|
78
|
+
"--config",
|
|
79
|
+
configPath,
|
|
80
|
+
"--port",
|
|
81
|
+
String(port),
|
|
82
|
+
...(attach ? ["--attach"] : []),
|
|
83
|
+
], { env: { ...process.env, ORBIT_HOME: root, ORBIT_CDP_PORT: String(port) } });
|
|
84
|
+
}
|
|
85
|
+
async function daemon(config) {
|
|
86
|
+
return (options.getDaemonStatus ?? daemonStatus)(daemonPaths(configPath, port), join(inside(root, config.orbit.runtime.path), "dist/cli.js"));
|
|
87
|
+
}
|
|
88
|
+
async function isRunning() {
|
|
89
|
+
const state = await daemon(await read());
|
|
90
|
+
if (state.conflict)
|
|
91
|
+
throw new Error("Orbit daemon ownership conflicts with another process; no files were changed");
|
|
92
|
+
return state.running;
|
|
93
|
+
}
|
|
94
|
+
async function mutate(operation, finalize) {
|
|
95
|
+
await prepare();
|
|
96
|
+
return withInstallationLock(configPath, async () => {
|
|
97
|
+
const previous = await read();
|
|
98
|
+
const config = structuredClone(previous);
|
|
99
|
+
const result = await operation(config);
|
|
100
|
+
const running = await isRunning();
|
|
101
|
+
if (running)
|
|
102
|
+
await runtimeCommand(previous, "stop");
|
|
103
|
+
try {
|
|
104
|
+
await atomicJson(configPath, config);
|
|
105
|
+
if (config.orbit.runtime.path !== previous.orbit.runtime.path)
|
|
106
|
+
await repairEntry(config);
|
|
107
|
+
if (options.manageExtensions !== false)
|
|
108
|
+
await extensions.reconcile(previous, config);
|
|
109
|
+
if (running)
|
|
110
|
+
await runtimeCommand(config, "start", true);
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
// Never leave a failed candidate running against restored metadata.
|
|
114
|
+
try {
|
|
115
|
+
if (await isRunning())
|
|
116
|
+
await runtimeCommand(config, "stop");
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
throw new Error("Candidate cleanup failed; installation recovery requires inspection", {
|
|
120
|
+
cause: error,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
await atomicJson(configPath, previous);
|
|
124
|
+
const recoveryErrors = [];
|
|
125
|
+
try {
|
|
126
|
+
if (config.orbit.runtime.path !== previous.orbit.runtime.path)
|
|
127
|
+
await repairEntry(previous);
|
|
128
|
+
if (options.manageExtensions !== false)
|
|
129
|
+
await extensions.reconcile(config, previous);
|
|
130
|
+
}
|
|
131
|
+
catch (recoveryError) {
|
|
132
|
+
recoveryErrors.push(recoveryError);
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
if (running)
|
|
136
|
+
await runtimeCommand(previous, "start", true);
|
|
137
|
+
}
|
|
138
|
+
catch (recoveryError) {
|
|
139
|
+
recoveryErrors.push(recoveryError);
|
|
140
|
+
}
|
|
141
|
+
if (recoveryErrors.length)
|
|
142
|
+
throw new AggregateError([error, ...recoveryErrors], `Installation failed: ${error instanceof Error ? error.message : String(error)}; configuration restored but recovery needs attention`, { cause: error });
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
await finalize?.();
|
|
146
|
+
return result;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
async function stageRuntime(candidate) {
|
|
150
|
+
const manifest = JSON.parse(await readFile(join(candidate, "package.json"), "utf8"));
|
|
151
|
+
if (manifest.name !== "@c0sc0s/orbit" || !/^\d+\.\d+\.\d+$/u.test(manifest.version))
|
|
152
|
+
throw new Error("Expected a built Orbit package");
|
|
153
|
+
const path = `runtime/${manifest.version}-${randomUUID()}`;
|
|
154
|
+
const destination = inside(root, path);
|
|
155
|
+
try {
|
|
156
|
+
await snapshotPackage(candidate, destination);
|
|
157
|
+
await run(process.execPath, [join(destination, "dist/cli.js"), "--help"]);
|
|
158
|
+
return { version: manifest.version, path };
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
await rm(destination, { recursive: true, force: true });
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
async function repairEntry(config) {
|
|
166
|
+
const runtimeIcon = join(inside(root, config.orbit.runtime.path), "assets/icon.icns");
|
|
167
|
+
await cp(join(inside(root, config.orbit.runtime.path), "dist/installation/entry.js"), join(root, "orbit.mjs"));
|
|
168
|
+
if ((options.platform ?? process.platform) === "darwin")
|
|
169
|
+
await createLauncher({
|
|
170
|
+
launcherPath,
|
|
171
|
+
nodePath: process.execPath,
|
|
172
|
+
cliPath: join(root, "orbit.mjs"),
|
|
173
|
+
configPath,
|
|
174
|
+
port,
|
|
175
|
+
logPath: join(root, "launcher.log"),
|
|
176
|
+
bundleId: "io.github.c0sc0s.orbit",
|
|
177
|
+
iconPath: await exists(runtimeIcon) ? runtimeIcon : join(source, "assets/icon.icns"),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
async function install() {
|
|
181
|
+
await prepare();
|
|
182
|
+
return withInstallationLock(configPath, async () => {
|
|
183
|
+
if (await exists(configPath)) {
|
|
184
|
+
const current = await read();
|
|
185
|
+
await repairEntry(current);
|
|
186
|
+
return { status: "already-installed", root, launcherPath };
|
|
187
|
+
}
|
|
188
|
+
const runtime = await stageRuntime(source);
|
|
189
|
+
const config = {
|
|
190
|
+
apiVersion: 1,
|
|
191
|
+
plugins: [],
|
|
192
|
+
orbit: { schemaVersion: 1, runtime, packages: {} },
|
|
193
|
+
};
|
|
194
|
+
await atomicJson(configPath, config);
|
|
195
|
+
await repairEntry(config);
|
|
196
|
+
return { status: "installed", root, launcherPath, version: runtime.version };
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
async function update(candidate = source) {
|
|
200
|
+
return mutate(async (config) => {
|
|
201
|
+
const runtime = await stageRuntime(candidate);
|
|
202
|
+
for (const plugin of Object.values(config.orbit.packages))
|
|
203
|
+
assertCompatible(plugin.manifest, runtime.version);
|
|
204
|
+
config.orbit.previousRuntime = config.orbit.runtime;
|
|
205
|
+
config.orbit.runtime = runtime;
|
|
206
|
+
return { status: "updated", version: runtime.version };
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
async function rollback() {
|
|
210
|
+
return mutate(async (config) => {
|
|
211
|
+
const previous = config.orbit.previousRuntime;
|
|
212
|
+
if (!previous)
|
|
213
|
+
throw new Error("No previous Orbit runtime is available");
|
|
214
|
+
for (const plugin of Object.values(config.orbit.packages))
|
|
215
|
+
assertCompatible(plugin.manifest, previous.version);
|
|
216
|
+
config.orbit.previousRuntime = config.orbit.runtime;
|
|
217
|
+
config.orbit.runtime = previous;
|
|
218
|
+
return { status: "rolled-back", version: previous.version };
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
async function addPlugin(candidate, settings = {}) {
|
|
222
|
+
candidate = await realpath(candidate);
|
|
223
|
+
const manifest = await readPluginManifest(candidate);
|
|
224
|
+
let installedRecord;
|
|
225
|
+
return mutate(async (config) => {
|
|
226
|
+
assertCompatible(manifest, config.orbit.runtime.version);
|
|
227
|
+
const packageName = JSON.parse(await readFile(join(candidate, "package.json"), "utf8")).name;
|
|
228
|
+
const legacyIds = manifest.legacyIds ?? [];
|
|
229
|
+
const matches = [manifest.id, ...legacyIds].filter(id => Object.hasOwn(config.orbit.packages, id));
|
|
230
|
+
if (matches.length > 1)
|
|
231
|
+
throw new Error("Conflicting current and legacy plugin registrations");
|
|
232
|
+
const previousId = matches[0] ?? manifest.id;
|
|
233
|
+
const existing = Object.hasOwn(config.orbit.packages, previousId)
|
|
234
|
+
? config.orbit.packages[previousId]
|
|
235
|
+
: undefined;
|
|
236
|
+
const previousEntry = config.plugins.find(plugin => plugin.id === previousId);
|
|
237
|
+
if (Object.entries(config.orbit.packages).some(([id, plugin]) => id !== previousId && (plugin.manifest.legacyIds ?? []).some(alias => [manifest.id, ...legacyIds].includes(alias))))
|
|
238
|
+
throw new Error("Plugin alias belongs to another package");
|
|
239
|
+
if (existing && JSON.stringify(existing.manifest.codex) !== JSON.stringify(manifest.codex))
|
|
240
|
+
throw new Error("Official extension identity cannot change during a plugin update");
|
|
241
|
+
if (manifest.codex &&
|
|
242
|
+
Object.entries(config.orbit.packages).some(([id, plugin]) => id !== previousId &&
|
|
243
|
+
plugin.manifest.codex?.marketplace === manifest.codex?.marketplace))
|
|
244
|
+
throw new Error("Official marketplace identity belongs to another plugin");
|
|
245
|
+
if (existing && existing.packageName !== packageName)
|
|
246
|
+
throw new Error("Plugin ID belongs to another package");
|
|
247
|
+
if ((!existing && previousEntry) || config.plugins.some((plugin) => [manifest.id, ...legacyIds].includes(plugin.id) && plugin.id !== previousEntry?.id))
|
|
248
|
+
throw new Error("Plugin ID belongs to an unmanaged module");
|
|
249
|
+
const path = `plugins/${manifest.id}/${manifest.version}-${randomUUID()}`;
|
|
250
|
+
const destination = inside(root, path);
|
|
251
|
+
await snapshotPackage(candidate, destination);
|
|
252
|
+
await readPluginManifest(destination);
|
|
253
|
+
const dataDirectory = existing?.dataDirectory ?? settings.dataDirectory ?? join(root, "data", manifest.id);
|
|
254
|
+
for (const protectedPath of [
|
|
255
|
+
home,
|
|
256
|
+
root,
|
|
257
|
+
source,
|
|
258
|
+
process.env.CODEX_HOME ?? join(home, ".codex"),
|
|
259
|
+
]) {
|
|
260
|
+
if (resolve(dataDirectory) === resolve(protectedPath) ||
|
|
261
|
+
resolve(protectedPath).startsWith(`${resolve(dataDirectory)}/`))
|
|
262
|
+
throw new Error("Unsafe plugin data directory");
|
|
263
|
+
}
|
|
264
|
+
await mkdir(dataDirectory, { recursive: true, mode: 0o700 });
|
|
265
|
+
const record = {
|
|
266
|
+
manifest,
|
|
267
|
+
path,
|
|
268
|
+
packageName,
|
|
269
|
+
dataDirectory,
|
|
270
|
+
externalData: (existing && previousId !== manifest.id) ? true : existing?.externalData ?? Boolean(settings.dataDirectory),
|
|
271
|
+
};
|
|
272
|
+
installedRecord = record;
|
|
273
|
+
delete config.orbit.packages[previousId];
|
|
274
|
+
config.orbit.packages[manifest.id] = record;
|
|
275
|
+
await atomicJson(join(destination, "orbit-installation.json"), {
|
|
276
|
+
platformRoot: root,
|
|
277
|
+
dataDirectory,
|
|
278
|
+
});
|
|
279
|
+
const entry = {
|
|
280
|
+
id: manifest.id,
|
|
281
|
+
version: manifest.runtimeVersion,
|
|
282
|
+
entry: `${path}/${manifest.renderer}`,
|
|
283
|
+
...(manifest.service ? { service: `${path}/${manifest.service}` } : {}),
|
|
284
|
+
enabled: previousEntry?.enabled ?? true,
|
|
285
|
+
config: {
|
|
286
|
+
...previousEntry?.config,
|
|
287
|
+
dataDirectory,
|
|
288
|
+
packageVersion: manifest.version,
|
|
289
|
+
platformRoot: root,
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
config.plugins = [...config.plugins.filter((plugin) => plugin.id !== previousId), entry];
|
|
293
|
+
return {
|
|
294
|
+
status: "registered",
|
|
295
|
+
id: manifest.id,
|
|
296
|
+
version: manifest.version,
|
|
297
|
+
path: destination,
|
|
298
|
+
};
|
|
299
|
+
}, async () => {
|
|
300
|
+
if (installedRecord)
|
|
301
|
+
await atomicJson(join(installedRecord.dataDirectory, "install.json"), {
|
|
302
|
+
packageVersion: manifest.version,
|
|
303
|
+
pluginVersion: manifest.version,
|
|
304
|
+
platformRoot: root,
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
async function setEnabled(id, enabled) {
|
|
309
|
+
return mutate(async (config) => {
|
|
310
|
+
id = resolvePluginId(config, id);
|
|
311
|
+
if (!Object.hasOwn(config.orbit.packages, id))
|
|
312
|
+
throw new Error(`Unknown plugin ${id}`);
|
|
313
|
+
const plugin = config.plugins.find((plugin) => plugin.id === id);
|
|
314
|
+
plugin.enabled = enabled;
|
|
315
|
+
return { status: enabled ? "enabled" : "disabled", id };
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
async function uninstallPlugin(id, purge = false) {
|
|
319
|
+
let retired;
|
|
320
|
+
const result = await mutate(async (config) => {
|
|
321
|
+
id = resolvePluginId(config, id);
|
|
322
|
+
retired = Object.hasOwn(config.orbit.packages, id) ? config.orbit.packages[id] : undefined;
|
|
323
|
+
if (!retired)
|
|
324
|
+
throw new Error(`Unknown plugin ${id}`);
|
|
325
|
+
if (purge &&
|
|
326
|
+
(retired.externalData || resolve(retired.dataDirectory) !== join(root, "data", id)))
|
|
327
|
+
throw new Error("Migrated external data must be removed explicitly; Orbit will not recursively delete it");
|
|
328
|
+
delete config.orbit.packages[id];
|
|
329
|
+
config.plugins = config.plugins.filter((plugin) => plugin.id !== id);
|
|
330
|
+
return { status: "uninstalled", id, settingsPreserved: !purge };
|
|
331
|
+
}, async () => {
|
|
332
|
+
await rm(inside(root, `plugins/${id}`), { recursive: true, force: true });
|
|
333
|
+
await rm(inside(root, `marketplaces/${id}`), { recursive: true, force: true });
|
|
334
|
+
if (purge && retired)
|
|
335
|
+
await rm(inside(root, relative(root, retired.dataDirectory)), {
|
|
336
|
+
recursive: true,
|
|
337
|
+
force: true,
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
return result;
|
|
341
|
+
}
|
|
342
|
+
async function installPackage(spec, expectedId) {
|
|
343
|
+
return withRegistryPackage(spec, run, async (candidate) => {
|
|
344
|
+
const manifest = await readPluginManifest(candidate);
|
|
345
|
+
if (expectedId && manifest.id !== expectedId && !manifest.legacyIds?.includes(expectedId))
|
|
346
|
+
throw new Error("Plugin update changed identity");
|
|
347
|
+
return addPlugin(candidate);
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
async function updatePackage() {
|
|
351
|
+
return withRegistryPackage("@c0sc0s/orbit@latest", run, (candidate) => update(candidate));
|
|
352
|
+
}
|
|
353
|
+
async function migrateLegacy(directory) {
|
|
354
|
+
await prepare();
|
|
355
|
+
return withInstallationLock(configPath, async () => {
|
|
356
|
+
await read();
|
|
357
|
+
return retireLegacyTags({
|
|
358
|
+
directory,
|
|
359
|
+
platformRoot: root,
|
|
360
|
+
home,
|
|
361
|
+
applicationsRoot: options.applicationsRoot,
|
|
362
|
+
port,
|
|
363
|
+
run,
|
|
364
|
+
codexBinary: options.codexBinary,
|
|
365
|
+
manageExtensions: options.manageExtensions,
|
|
366
|
+
platform: options.platform ?? process.platform,
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
async function start(attach = false) {
|
|
371
|
+
return runtimeCommand(await read(), "start", attach);
|
|
372
|
+
}
|
|
373
|
+
async function status() {
|
|
374
|
+
if (!(await exists(configPath)))
|
|
375
|
+
return { installed: false, root };
|
|
376
|
+
const config = await read();
|
|
377
|
+
return {
|
|
378
|
+
installed: true,
|
|
379
|
+
root,
|
|
380
|
+
runtime: config.orbit.runtime,
|
|
381
|
+
daemon: await daemon(config),
|
|
382
|
+
plugins: config.plugins,
|
|
383
|
+
packages: config.orbit.packages,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
async function extensionStatus(id) {
|
|
387
|
+
const config = await read();
|
|
388
|
+
const record = config.orbit.packages[resolvePluginId(config, id)];
|
|
389
|
+
const extension = record?.manifest.codex;
|
|
390
|
+
if (!extension)
|
|
391
|
+
return { installed: false, enabled: false, payloadPresent: false };
|
|
392
|
+
try {
|
|
393
|
+
const state = JSON.parse((await extensions.command(["plugin", "list", "--json"])).stdout);
|
|
394
|
+
const plugin = state.installed?.find((value) => value.pluginId === `${extension.name}@${extension.marketplace}`);
|
|
395
|
+
const payload = plugin?.source?.path;
|
|
396
|
+
return {
|
|
397
|
+
installed: Boolean(plugin),
|
|
398
|
+
enabled: plugin?.enabled === true,
|
|
399
|
+
payloadPresent: typeof payload === "string" && (await exists(join(payload, ".codex-plugin/plugin.json"))),
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
catch (error) {
|
|
403
|
+
return {
|
|
404
|
+
installed: false,
|
|
405
|
+
enabled: false,
|
|
406
|
+
payloadPresent: false,
|
|
407
|
+
error: error instanceof Error ? error.message : "Official extension status unavailable",
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
async function doctor() {
|
|
412
|
+
const checks = [{ id: "node", ok: Number(process.versions.node.split(".")[0]) >= 22 }];
|
|
413
|
+
const state = await status();
|
|
414
|
+
checks.push({ id: "installed", ok: state.installed });
|
|
415
|
+
if (state.installed && state.runtime) {
|
|
416
|
+
checks.push({
|
|
417
|
+
id: "runtime",
|
|
418
|
+
ok: await exists(join(root, state.runtime.path, "dist/cli.js")),
|
|
419
|
+
});
|
|
420
|
+
checks.push({
|
|
421
|
+
id: "launcher",
|
|
422
|
+
ok: (options.platform ?? process.platform) !== "darwin" || (await exists(launcherPath)),
|
|
423
|
+
});
|
|
424
|
+
if ((options.platform ?? process.platform) === "darwin") {
|
|
425
|
+
let ok = false;
|
|
426
|
+
try {
|
|
427
|
+
const icon = await readFile(join(launcherPath, "Contents/Resources/icon.icns"));
|
|
428
|
+
const plist = await readFile(join(launcherPath, "Contents/Info.plist"), "utf8");
|
|
429
|
+
ok = icon.length >= 8 && icon.subarray(0, 4).toString() === "icns" &&
|
|
430
|
+
icon.readUInt32BE(4) === icon.length &&
|
|
431
|
+
/<key>CFBundleIconFile<\/key>\s*<string>icon.icns<\/string>/u.test(plist);
|
|
432
|
+
}
|
|
433
|
+
catch { /* A missing or invalid icon is reported as a failed diagnostic. */ }
|
|
434
|
+
checks.push({ id: "launcher-icon", ok });
|
|
435
|
+
}
|
|
436
|
+
checks.push({ id: "owner", ok: !state.daemon?.conflict });
|
|
437
|
+
for (const record of Object.values(state.packages ?? {})) {
|
|
438
|
+
let ok = true;
|
|
439
|
+
try {
|
|
440
|
+
assertCompatible(await readPluginManifest(join(root, record.path)), state.runtime.version);
|
|
441
|
+
}
|
|
442
|
+
catch {
|
|
443
|
+
ok = false;
|
|
444
|
+
}
|
|
445
|
+
checks.push({ id: `plugin:${record.manifest.id}`, ok });
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return { ok: checks.every((check) => check.ok), checks, status: state };
|
|
449
|
+
}
|
|
450
|
+
return {
|
|
451
|
+
paths: { root, configPath, launcherPath },
|
|
452
|
+
install,
|
|
453
|
+
update,
|
|
454
|
+
updatePackage,
|
|
455
|
+
rollback,
|
|
456
|
+
migrateLegacy,
|
|
457
|
+
addPlugin,
|
|
458
|
+
setEnabled,
|
|
459
|
+
uninstallPlugin,
|
|
460
|
+
installPackage,
|
|
461
|
+
start,
|
|
462
|
+
status,
|
|
463
|
+
doctor,
|
|
464
|
+
extensionStatus,
|
|
465
|
+
read,
|
|
466
|
+
run,
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
//# sourceMappingURL=manager.js.map
|