pilotswarm 0.0.1 → 0.4.1
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 +37 -1
- package/mcp/README.md +484 -0
- package/mcp/dist/bin/pilotswarm-mcp.d.ts +3 -0
- package/mcp/dist/bin/pilotswarm-mcp.d.ts.map +1 -0
- package/mcp/dist/bin/pilotswarm-mcp.js +367 -0
- package/mcp/dist/bin/pilotswarm-mcp.js.map +1 -0
- package/mcp/dist/src/auth.d.ts +7 -0
- package/mcp/dist/src/auth.d.ts.map +1 -0
- package/mcp/dist/src/auth.js +99 -0
- package/mcp/dist/src/auth.js.map +1 -0
- package/mcp/dist/src/context.d.ts +48 -0
- package/mcp/dist/src/context.d.ts.map +1 -0
- package/mcp/dist/src/context.js +83 -0
- package/mcp/dist/src/context.js.map +1 -0
- package/mcp/dist/src/index.d.ts +4 -0
- package/mcp/dist/src/index.d.ts.map +1 -0
- package/mcp/dist/src/index.js +3 -0
- package/mcp/dist/src/index.js.map +1 -0
- package/mcp/dist/src/prompts/skills.d.ts +4 -0
- package/mcp/dist/src/prompts/skills.d.ts.map +1 -0
- package/mcp/dist/src/prompts/skills.js +11 -0
- package/mcp/dist/src/prompts/skills.js.map +1 -0
- package/mcp/dist/src/resources/agents.d.ts +4 -0
- package/mcp/dist/src/resources/agents.d.ts.map +1 -0
- package/mcp/dist/src/resources/agents.js +64 -0
- package/mcp/dist/src/resources/agents.js.map +1 -0
- package/mcp/dist/src/resources/facts.d.ts +4 -0
- package/mcp/dist/src/resources/facts.d.ts.map +1 -0
- package/mcp/dist/src/resources/facts.js +125 -0
- package/mcp/dist/src/resources/facts.js.map +1 -0
- package/mcp/dist/src/resources/models.d.ts +4 -0
- package/mcp/dist/src/resources/models.d.ts.map +1 -0
- package/mcp/dist/src/resources/models.js +43 -0
- package/mcp/dist/src/resources/models.js.map +1 -0
- package/mcp/dist/src/resources/sessions.d.ts +4 -0
- package/mcp/dist/src/resources/sessions.d.ts.map +1 -0
- package/mcp/dist/src/resources/sessions.js +190 -0
- package/mcp/dist/src/resources/sessions.js.map +1 -0
- package/mcp/dist/src/resources/subscriptions.d.ts +9 -0
- package/mcp/dist/src/resources/subscriptions.d.ts.map +1 -0
- package/mcp/dist/src/resources/subscriptions.js +157 -0
- package/mcp/dist/src/resources/subscriptions.js.map +1 -0
- package/mcp/dist/src/server.d.ts +4 -0
- package/mcp/dist/src/server.d.ts.map +1 -0
- package/mcp/dist/src/server.js +59 -0
- package/mcp/dist/src/server.js.map +1 -0
- package/mcp/dist/src/tools/agents.d.ts +4 -0
- package/mcp/dist/src/tools/agents.d.ts.map +1 -0
- package/mcp/dist/src/tools/agents.js +317 -0
- package/mcp/dist/src/tools/agents.js.map +1 -0
- package/mcp/dist/src/tools/facts.d.ts +4 -0
- package/mcp/dist/src/tools/facts.d.ts.map +1 -0
- package/mcp/dist/src/tools/facts.js +151 -0
- package/mcp/dist/src/tools/facts.js.map +1 -0
- package/mcp/dist/src/tools/models.d.ts +4 -0
- package/mcp/dist/src/tools/models.d.ts.map +1 -0
- package/mcp/dist/src/tools/models.js +256 -0
- package/mcp/dist/src/tools/models.js.map +1 -0
- package/mcp/dist/src/tools/sessions.d.ts +4 -0
- package/mcp/dist/src/tools/sessions.d.ts.map +1 -0
- package/mcp/dist/src/tools/sessions.js +606 -0
- package/mcp/dist/src/tools/sessions.js.map +1 -0
- package/mcp/dist/src/util/command.d.ts +52 -0
- package/mcp/dist/src/util/command.d.ts.map +1 -0
- package/mcp/dist/src/util/command.js +78 -0
- package/mcp/dist/src/util/command.js.map +1 -0
- package/package.json +82 -6
- package/tui/README.md +35 -0
- package/tui/bin/tui.js +30 -0
- package/tui/plugins/.mcp.json +7 -0
- package/tui/plugins/plugin.json +13 -0
- package/tui/plugins/session-policy.json +8 -0
- package/tui/src/app.js +850 -0
- package/tui/src/auth/cli.js +111 -0
- package/tui/src/auth/entra-auth.js +218 -0
- package/tui/src/bootstrap-env.js +176 -0
- package/tui/src/embedded-workers.js +79 -0
- package/tui/src/http-transport-host.js +106 -0
- package/tui/src/index.js +340 -0
- package/tui/src/node-sdk-transport.js +1794 -0
- package/tui/src/platform.js +984 -0
- package/tui/src/plugin-config.js +273 -0
- package/tui/src/portal.js +7 -0
- package/tui/src/version.js +7 -0
- package/tui/tui-splash-mobile.txt +7 -0
- package/tui/tui-splash.txt +11 -0
- package/ui/core/README.md +6 -0
- package/ui/core/src/commands.js +93 -0
- package/ui/core/src/context-usage.js +212 -0
- package/ui/core/src/controller.js +6201 -0
- package/ui/core/src/formatting.js +1036 -0
- package/ui/core/src/history.js +950 -0
- package/ui/core/src/index.js +13 -0
- package/ui/core/src/layout.js +332 -0
- package/ui/core/src/reducer.js +1952 -0
- package/ui/core/src/selectors.js +5419 -0
- package/ui/core/src/session-errors.js +14 -0
- package/ui/core/src/session-tree.js +151 -0
- package/ui/core/src/state.js +251 -0
- package/ui/core/src/store.js +23 -0
- package/ui/core/src/system-titles.js +24 -0
- package/ui/core/src/themes/catppuccin-mocha.js +56 -0
- package/ui/core/src/themes/cobalt2.js +56 -0
- package/ui/core/src/themes/dark-high-contrast.js +56 -0
- package/ui/core/src/themes/daylight.js +62 -0
- package/ui/core/src/themes/dracula.js +56 -0
- package/ui/core/src/themes/github-dark.js +56 -0
- package/ui/core/src/themes/github-light.js +59 -0
- package/ui/core/src/themes/gruvbox-dark.js +56 -0
- package/ui/core/src/themes/hacker-x-matrix.js +56 -0
- package/ui/core/src/themes/hacker-x-orion-prime.js +56 -0
- package/ui/core/src/themes/helpers.js +79 -0
- package/ui/core/src/themes/high-contrast-mono.js +59 -0
- package/ui/core/src/themes/index.js +52 -0
- package/ui/core/src/themes/light-high-contrast.js +62 -0
- package/ui/core/src/themes/noctis-obscuro.js +56 -0
- package/ui/core/src/themes/noctis.js +56 -0
- package/ui/core/src/themes/paper-ink.js +62 -0
- package/ui/core/src/themes/solarized-ops.js +59 -0
- package/ui/core/src/themes/terminal-green.js +59 -0
- package/ui/core/src/themes/tokyo-night.js +56 -0
- package/ui/react/README.md +5 -0
- package/ui/react/src/chat-status.js +39 -0
- package/ui/react/src/components.js +1989 -0
- package/ui/react/src/index.js +4 -0
- package/ui/react/src/platform.js +15 -0
- package/ui/react/src/use-controller-state.js +38 -0
- package/ui/react/src/web-app.js +4457 -0
- package/web/README.md +198 -0
- package/web/api/router.js +196 -0
- package/web/api/ws.js +152 -0
- package/web/auth/authz/engine.js +204 -0
- package/web/auth/config.js +115 -0
- package/web/auth/index.js +175 -0
- package/web/auth/normalize/entra.js +22 -0
- package/web/auth/providers/entra.js +76 -0
- package/web/auth/providers/none.js +24 -0
- package/web/auth.js +10 -0
- package/web/bin/serve.js +53 -0
- package/web/config.js +20 -0
- package/web/dist/app.js +469 -0
- package/web/dist/assets/index-DmGOcKR-.css +1 -0
- package/web/dist/assets/index-xJ8IzIZY.js +24 -0
- package/web/dist/assets/msal-CytV9RFv.js +7 -0
- package/web/dist/assets/pilotswarm-D9pEmenA.js +90 -0
- package/web/dist/assets/react-CEPDSRB6.js +1 -0
- package/web/dist/index.html +16 -0
- package/web/runtime.js +455 -0
- package/web/server.js +276 -0
- package/index.js +0 -1
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const pkgRoot = path.resolve(__dirname, "..");
|
|
7
|
+
const defaultTuiSplashPath = path.join(pkgRoot, "tui-splash.txt");
|
|
8
|
+
const defaultTuiSplashMobilePath = path.join(pkgRoot, "tui-splash-mobile.txt");
|
|
9
|
+
|
|
10
|
+
function fileExists(filePath) {
|
|
11
|
+
try {
|
|
12
|
+
return fs.existsSync(filePath);
|
|
13
|
+
} catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function readOptionalTextFile(filePath) {
|
|
19
|
+
try {
|
|
20
|
+
return fs.readFileSync(filePath, "utf-8").trimEnd();
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getObject(value) {
|
|
27
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
28
|
+
? value
|
|
29
|
+
: {};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function firstNonEmptyString(...values) {
|
|
33
|
+
for (const value of values) {
|
|
34
|
+
if (typeof value === "string" && value.trim()) {
|
|
35
|
+
return value.trim();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function resolveRelativePath(baseDir, relativePath) {
|
|
42
|
+
if (!baseDir || typeof relativePath !== "string" || !relativePath.trim()) return null;
|
|
43
|
+
const basePath = path.resolve(baseDir);
|
|
44
|
+
const filePath = path.resolve(basePath, relativePath);
|
|
45
|
+
if (filePath !== basePath && !filePath.startsWith(`${basePath}${path.sep}`)) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return filePath;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function readRelativeTextFile(baseDir, relativePath) {
|
|
52
|
+
const filePath = resolveRelativePath(baseDir, relativePath);
|
|
53
|
+
if (!filePath) return null;
|
|
54
|
+
return readOptionalTextFile(filePath);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function resolveRelativeAssetFile(baseDir, relativePath) {
|
|
58
|
+
const filePath = resolveRelativePath(baseDir, relativePath);
|
|
59
|
+
if (!filePath || !fileExists(filePath)) return null;
|
|
60
|
+
return filePath;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function firstAssetUrl(...values) {
|
|
64
|
+
for (const value of values) {
|
|
65
|
+
if (typeof value !== "string" || !value.trim()) continue;
|
|
66
|
+
const trimmed = value.trim();
|
|
67
|
+
if (/^(https?:\/\/|\/|data:|blob:)/iu.test(trimmed)) {
|
|
68
|
+
return trimmed;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function resolvePortalAsset(baseDir, { file, url }) {
|
|
75
|
+
const directUrl = firstAssetUrl(url);
|
|
76
|
+
if (directUrl) {
|
|
77
|
+
return { filePath: null, publicUrl: directUrl };
|
|
78
|
+
}
|
|
79
|
+
const filePath = resolveRelativeAssetFile(baseDir, file);
|
|
80
|
+
if (!filePath) {
|
|
81
|
+
return { filePath: null, publicUrl: null };
|
|
82
|
+
}
|
|
83
|
+
return { filePath, publicUrl: null };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function readSplashValue(baseDir, config, fallback) {
|
|
87
|
+
if (typeof config?.splash === "string" && config.splash.trim()) {
|
|
88
|
+
return config.splash;
|
|
89
|
+
}
|
|
90
|
+
if (typeof config?.splashFile === "string" && config.splashFile.trim()) {
|
|
91
|
+
const fileText = readRelativeTextFile(baseDir, config.splashFile);
|
|
92
|
+
if (fileText != null) return fileText;
|
|
93
|
+
}
|
|
94
|
+
return fallback;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Narrow-viewport splash variant (splashMobile / splashMobileFile). No
|
|
98
|
+
// default art: null means "wrap the main splash when it does not fit".
|
|
99
|
+
function readSplashMobileValue(baseDir, config, fallback = null) {
|
|
100
|
+
if (typeof config?.splashMobile === "string" && config.splashMobile.trim()) {
|
|
101
|
+
return config.splashMobile;
|
|
102
|
+
}
|
|
103
|
+
if (typeof config?.splashMobileFile === "string" && config.splashMobileFile.trim()) {
|
|
104
|
+
const fileText = readRelativeTextFile(baseDir, config.splashMobileFile);
|
|
105
|
+
if (fileText != null) return fileText;
|
|
106
|
+
}
|
|
107
|
+
return fallback;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getDefaultSplash() {
|
|
111
|
+
return readOptionalTextFile(defaultTuiSplashPath) || "{bold}{cyan-fg}PilotSwarm{/cyan-fg}{/bold}";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function getDefaultSplashMobile() {
|
|
115
|
+
return readOptionalTextFile(defaultTuiSplashMobilePath) || null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function readPluginMetadata(pluginDir) {
|
|
119
|
+
if (!pluginDir) return null;
|
|
120
|
+
const pluginJsonPath = path.join(pluginDir, "plugin.json");
|
|
121
|
+
if (!fileExists(pluginJsonPath)) return null;
|
|
122
|
+
try {
|
|
123
|
+
return JSON.parse(fs.readFileSync(pluginJsonPath, "utf-8"));
|
|
124
|
+
} catch (error) {
|
|
125
|
+
throw new Error(`Failed to parse plugin metadata: ${pluginJsonPath}: ${error.message}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function getPluginDirsFromEnv() {
|
|
130
|
+
const envDirs = String(process.env.PLUGIN_DIRS || "")
|
|
131
|
+
.split(",")
|
|
132
|
+
.map((value) => value.trim())
|
|
133
|
+
.filter(Boolean)
|
|
134
|
+
.map((value) => path.resolve(value));
|
|
135
|
+
if (envDirs.length > 0) return envDirs;
|
|
136
|
+
|
|
137
|
+
const cwdPlugin = path.resolve(process.cwd(), "plugins");
|
|
138
|
+
if (fileExists(cwdPlugin)) return [cwdPlugin];
|
|
139
|
+
|
|
140
|
+
const bundledPlugin = path.join(pkgRoot, "plugins");
|
|
141
|
+
if (fileExists(bundledPlugin)) return [bundledPlugin];
|
|
142
|
+
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function resolveTuiBranding(pluginDir) {
|
|
147
|
+
const pluginMeta = readPluginMetadata(pluginDir);
|
|
148
|
+
const tui = pluginMeta?.tui;
|
|
149
|
+
const defaultSplash = getDefaultSplash();
|
|
150
|
+
if (!tui || typeof tui !== "object") {
|
|
151
|
+
const defaultMobile = getDefaultSplashMobile();
|
|
152
|
+
return { title: "PilotSwarm", splash: defaultSplash, ...(defaultMobile ? { splashMobile: defaultMobile } : {}) };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const title = firstNonEmptyString(tui.title, "PilotSwarm") || "PilotSwarm";
|
|
156
|
+
const splash = readSplashValue(pluginDir, tui, defaultSplash);
|
|
157
|
+
// The default mobile art only pairs with the default splash — an app that
|
|
158
|
+
// ships its own splash without a mobile variant wraps instead of showing
|
|
159
|
+
// PilotSwarm-branded art.
|
|
160
|
+
const splashMobile = readSplashMobileValue(pluginDir, tui, splash === defaultSplash ? getDefaultSplashMobile() : null);
|
|
161
|
+
return { title, splash, ...(splashMobile ? { splashMobile } : {}) };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function resolvePortalConfigBundleFromPluginDirs(pluginDirs = []) {
|
|
165
|
+
const defaultSplash = getDefaultSplash();
|
|
166
|
+
const defaults = {
|
|
167
|
+
branding: {
|
|
168
|
+
title: "PilotSwarm",
|
|
169
|
+
pageTitle: "PilotSwarm",
|
|
170
|
+
splash: defaultSplash,
|
|
171
|
+
splashMobile: getDefaultSplashMobile(),
|
|
172
|
+
logoUrl: null,
|
|
173
|
+
faviconUrl: null,
|
|
174
|
+
},
|
|
175
|
+
ui: {
|
|
176
|
+
loadingMessage: "Preparing your workspace",
|
|
177
|
+
loadingCopy: "Connecting the shared workspace and live session feeds...",
|
|
178
|
+
},
|
|
179
|
+
auth: {
|
|
180
|
+
provider: null,
|
|
181
|
+
providers: {},
|
|
182
|
+
signInTitle: "Sign in to PilotSwarm",
|
|
183
|
+
signInMessage: null,
|
|
184
|
+
signInLabel: "Sign In",
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
for (const pluginDir of pluginDirs) {
|
|
189
|
+
const absDir = path.resolve(pluginDir);
|
|
190
|
+
const pluginMeta = readPluginMetadata(absDir);
|
|
191
|
+
if (!pluginMeta) continue;
|
|
192
|
+
|
|
193
|
+
const portal = getObject(pluginMeta?.portal);
|
|
194
|
+
const portalBranding = getObject(portal.branding);
|
|
195
|
+
const portalUi = getObject(portal.ui);
|
|
196
|
+
const portalAuth = getObject(portal.auth);
|
|
197
|
+
const tui = getObject(pluginMeta?.tui);
|
|
198
|
+
|
|
199
|
+
const title = firstNonEmptyString(portalBranding.title, portal.title, tui.title, defaults.branding.title) || defaults.branding.title;
|
|
200
|
+
const pageTitle = firstNonEmptyString(portalBranding.pageTitle, portal.pageTitle, title, defaults.branding.pageTitle) || defaults.branding.pageTitle;
|
|
201
|
+
const splash = readSplashValue(
|
|
202
|
+
absDir,
|
|
203
|
+
portalBranding,
|
|
204
|
+
readSplashValue(absDir, portal, readSplashValue(absDir, tui, defaults.branding.splash)),
|
|
205
|
+
);
|
|
206
|
+
const splashMobile = readSplashMobileValue(
|
|
207
|
+
absDir,
|
|
208
|
+
portalBranding,
|
|
209
|
+
readSplashMobileValue(
|
|
210
|
+
absDir,
|
|
211
|
+
portal,
|
|
212
|
+
readSplashMobileValue(absDir, tui, splash === defaultSplash ? getDefaultSplashMobile() : null),
|
|
213
|
+
),
|
|
214
|
+
);
|
|
215
|
+
const logoAsset = resolvePortalAsset(absDir, {
|
|
216
|
+
file: firstNonEmptyString(portalBranding.logoFile, portal.logoFile),
|
|
217
|
+
url: firstNonEmptyString(portalBranding.logoUrl, portal.logoUrl),
|
|
218
|
+
});
|
|
219
|
+
const faviconAsset = resolvePortalAsset(absDir, {
|
|
220
|
+
file: firstNonEmptyString(portalBranding.faviconFile, portal.faviconFile, portalBranding.logoFile, portal.logoFile),
|
|
221
|
+
url: firstNonEmptyString(portalBranding.faviconUrl, portal.faviconUrl, portalBranding.logoUrl, portal.logoUrl),
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const assetFiles = {};
|
|
225
|
+
const branding = {
|
|
226
|
+
title,
|
|
227
|
+
pageTitle,
|
|
228
|
+
splash,
|
|
229
|
+
splashMobile: splashMobile || null,
|
|
230
|
+
logoUrl: logoAsset.publicUrl || null,
|
|
231
|
+
faviconUrl: faviconAsset.publicUrl || null,
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
if (logoAsset.filePath) {
|
|
235
|
+
assetFiles.logo = logoAsset.filePath;
|
|
236
|
+
branding.logoUrl = "/api/portal-assets/logo";
|
|
237
|
+
}
|
|
238
|
+
if (faviconAsset.filePath) {
|
|
239
|
+
assetFiles.favicon = faviconAsset.filePath;
|
|
240
|
+
branding.faviconUrl = "/api/portal-assets/favicon";
|
|
241
|
+
}
|
|
242
|
+
if (!branding.faviconUrl && branding.logoUrl) {
|
|
243
|
+
branding.faviconUrl = branding.logoUrl;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
portalConfig: {
|
|
248
|
+
branding,
|
|
249
|
+
ui: {
|
|
250
|
+
loadingMessage: firstNonEmptyString(portalUi.loadingMessage, portal.loadingMessage, defaults.ui.loadingMessage) || defaults.ui.loadingMessage,
|
|
251
|
+
loadingCopy: firstNonEmptyString(portalUi.loadingCopy, portal.loadingCopy, defaults.ui.loadingCopy) || defaults.ui.loadingCopy,
|
|
252
|
+
},
|
|
253
|
+
auth: {
|
|
254
|
+
provider: firstNonEmptyString(portalAuth.provider, portal.provider),
|
|
255
|
+
providers: getObject(portalAuth.providers),
|
|
256
|
+
signInTitle: firstNonEmptyString(portalAuth.signInTitle, portal.signInTitle, `Sign in to ${title}`) || `Sign in to ${title}`,
|
|
257
|
+
signInMessage: firstNonEmptyString(portalAuth.signInMessage, portal.signInMessage, defaults.auth.signInMessage),
|
|
258
|
+
signInLabel: firstNonEmptyString(portalAuth.signInLabel, defaults.auth.signInLabel) || defaults.auth.signInLabel,
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
assetFiles,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
portalConfig: defaults,
|
|
267
|
+
assetFiles: {},
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function resolvePortalConfigFromPluginDirs(pluginDirs = []) {
|
|
272
|
+
return resolvePortalConfigBundleFromPluginDirs(pluginDirs).portalConfig;
|
|
273
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
|
|
3
|
+
const require = createRequire(import.meta.url);
|
|
4
|
+
const cliPackageJson = require("../../package.json");
|
|
5
|
+
|
|
6
|
+
export const PILOTSWARM_CLI_VERSION = String(cliPackageJson?.version || "0.0.0");
|
|
7
|
+
export const PILOTSWARM_CLI_VERSION_LABEL = `v${PILOTSWARM_CLI_VERSION}`;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{bold}{cyan-fg}█▀█ █ █ █▀█ ▀█▀{/cyan-fg} {magenta-fg}█▀ █ █ █ ▄▀█ █▀█ █▀▄▀█{/magenta-fg}{/bold}
|
|
2
|
+
{bold}{cyan-fg}█▀▀ █ █▄▄ █▄█ █ {/cyan-fg} {magenta-fg}▄█ ▀▄▀▄▀ █▀█ █▀▄ █ ▀ █{/magenta-fg}{/bold}
|
|
3
|
+
{yellow-fg}░▒▓██████████████████████████████████▓▒░{/yellow-fg}
|
|
4
|
+
{bold}{white-fg}Durable AI Agent Orchestration{/white-fg}{/bold}
|
|
5
|
+
{cyan-fg}Crash recovery{/cyan-fg} · {magenta-fg}Durable timers{/magenta-fg}
|
|
6
|
+
{yellow-fg}Sub-agents{/yellow-fg} · {green-fg}Multi-node scaling{/green-fg}
|
|
7
|
+
{gray-fg}Powered by duroxide + Copilot SDK{/gray-fg}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{bold}{cyan-fg}
|
|
2
|
+
____ _ __ __ _____
|
|
3
|
+
/ __ \(_) /___ / /_/ ___/ ______ __________ ___
|
|
4
|
+
{/cyan-fg}{magenta-fg} / /_/ / / / __ \/ __/\__ \ | /| / / __ `/ ___/ __ `__ \
|
|
5
|
+
/ ____/ / / /_/ / /_ ___/ / |/ |/ / /_/ / / / / / / / /{/magenta-fg}
|
|
6
|
+
{yellow-fg}/_/ /_/_/\____/\__//____/|__/|__/\__,_/_/ /_/ /_/ /_/ {/yellow-fg}
|
|
7
|
+
{/bold}
|
|
8
|
+
|
|
9
|
+
{bold}{white-fg}Durable AI Agent Orchestration{/white-fg}{/bold}
|
|
10
|
+
{cyan-fg}Crash recovery{/cyan-fg} · {magenta-fg}Durable timers{/magenta-fg} · {yellow-fg}Sub-agents{/yellow-fg} · {green-fg}Multi-node scaling{/green-fg}
|
|
11
|
+
{gray-fg}Powered by duroxide + GitHub Copilot SDK{/gray-fg}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
export const FOCUS_REGIONS = {
|
|
2
|
+
SESSIONS: "sessions",
|
|
3
|
+
CHAT: "chat",
|
|
4
|
+
INSPECTOR: "inspector",
|
|
5
|
+
ACTIVITY: "activity",
|
|
6
|
+
PROMPT: "prompt",
|
|
7
|
+
ADMIN: "admin",
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const INSPECTOR_TABS = ["sequence", "logs", "nodes", "history", "files", "stats"];
|
|
11
|
+
|
|
12
|
+
export const UI_COMMANDS = {
|
|
13
|
+
REFRESH: "refresh",
|
|
14
|
+
NEW_SESSION: "newSession",
|
|
15
|
+
OPEN_MODEL_PICKER: "openModelPicker",
|
|
16
|
+
OPEN_THEME_PICKER: "openThemePicker",
|
|
17
|
+
OPEN_RENAME_SESSION: "openRenameSession",
|
|
18
|
+
OPEN_SESSION_FILTER: "openSessionFilter",
|
|
19
|
+
OPEN_TERMINATE_PICKER: "openTerminatePicker",
|
|
20
|
+
OPEN_MOVE_TO_GROUP: "openMoveToGroup",
|
|
21
|
+
CREATE_SESSION_GROUP: "createSessionGroup",
|
|
22
|
+
OPEN_ARTIFACT_UPLOAD: "openArtifactUpload",
|
|
23
|
+
TOGGLE_CHAT_VIEW: "toggleChatView",
|
|
24
|
+
CLOSE_MODAL: "closeModal",
|
|
25
|
+
MODAL_PREV: "modalPrev",
|
|
26
|
+
MODAL_NEXT: "modalNext",
|
|
27
|
+
MODAL_PANE_PREV: "modalPanePrev",
|
|
28
|
+
MODAL_PANE_NEXT: "modalPaneNext",
|
|
29
|
+
MODAL_CONFIRM: "modalConfirm",
|
|
30
|
+
SEND_PROMPT: "sendPrompt",
|
|
31
|
+
FOCUS_NEXT: "focusNext",
|
|
32
|
+
FOCUS_PREV: "focusPrev",
|
|
33
|
+
FOCUS_LEFT: "focusLeft",
|
|
34
|
+
FOCUS_RIGHT: "focusRight",
|
|
35
|
+
FOCUS_PROMPT: "focusPrompt",
|
|
36
|
+
FOCUS_SESSIONS: "focusSessions",
|
|
37
|
+
MOVE_SESSION_UP: "moveSessionUp",
|
|
38
|
+
MOVE_SESSION_DOWN: "moveSessionDown",
|
|
39
|
+
EXPAND_SESSION: "expandSession",
|
|
40
|
+
COLLAPSE_SESSION: "collapseSession",
|
|
41
|
+
NEXT_INSPECTOR_TAB: "nextInspectorTab",
|
|
42
|
+
PREV_INSPECTOR_TAB: "prevInspectorTab",
|
|
43
|
+
CYCLE_INSPECTOR_TAB: "cycleInspectorTab",
|
|
44
|
+
GROW_LEFT_PANE: "growLeftPane",
|
|
45
|
+
GROW_RIGHT_PANE: "growRightPane",
|
|
46
|
+
GROW_SESSION_PANE: "growSessionPane",
|
|
47
|
+
SHRINK_SESSION_PANE: "shrinkSessionPane",
|
|
48
|
+
OPEN_ARTIFACT_PICKER: "openArtifactPicker",
|
|
49
|
+
TOGGLE_LOG_TAIL: "toggleLogTail",
|
|
50
|
+
TOGGLE_STATS_VIEW: "toggleStatsView",
|
|
51
|
+
OPEN_LOG_FILTER: "openLogFilter",
|
|
52
|
+
OPEN_FILES_FILTER: "openFilesFilter",
|
|
53
|
+
MOVE_FILE_UP: "moveFileUp",
|
|
54
|
+
MOVE_FILE_DOWN: "moveFileDown",
|
|
55
|
+
DOWNLOAD_SELECTED_FILE: "downloadSelectedFile",
|
|
56
|
+
DELETE_SELECTED_FILE: "deleteSelectedFile",
|
|
57
|
+
OPEN_SELECTED_FILE: "openSelectedFile",
|
|
58
|
+
TOGGLE_FILE_PREVIEW_FULLSCREEN: "toggleFilePreviewFullscreen",
|
|
59
|
+
TOGGLE_PANE_FULLSCREEN: "togglePaneFullscreen",
|
|
60
|
+
SCROLL_UP: "scrollUp",
|
|
61
|
+
SCROLL_DOWN: "scrollDown",
|
|
62
|
+
PAGE_UP: "pageUp",
|
|
63
|
+
PAGE_DOWN: "pageDown",
|
|
64
|
+
EXPAND_HISTORY: "expandHistory",
|
|
65
|
+
SCROLL_TOP: "scrollTop",
|
|
66
|
+
SCROLL_BOTTOM: "scrollBottom",
|
|
67
|
+
DONE_SESSION: "doneSession",
|
|
68
|
+
CANCEL_SESSION: "cancelSession",
|
|
69
|
+
STOP_TURN: "stopTurn",
|
|
70
|
+
DELETE_SESSION: "deleteSession",
|
|
71
|
+
PIN_SESSION: "pinSession",
|
|
72
|
+
TOGGLE_SELECT_MODE: "toggleSelectMode",
|
|
73
|
+
TOGGLE_SESSION_SELECTION: "toggleSessionSelection",
|
|
74
|
+
CLEAR_SESSION_SELECTION: "clearSessionSelection",
|
|
75
|
+
OPEN_HISTORY_FORMAT: "openHistoryFormat",
|
|
76
|
+
REFRESH_EXECUTION_HISTORY: "refreshExecutionHistory",
|
|
77
|
+
EXPORT_EXECUTION_HISTORY: "exportExecutionHistory",
|
|
78
|
+
// Admin Console — swaps out sessions+chat for the per-user admin view.
|
|
79
|
+
OPEN_ADMIN_CONSOLE: "openAdminConsole",
|
|
80
|
+
CLOSE_ADMIN_CONSOLE: "closeAdminConsole",
|
|
81
|
+
ADMIN_REFRESH_PROFILE: "adminRefreshProfile",
|
|
82
|
+
ADMIN_BEGIN_EDIT_GHCP_KEY: "adminBeginEditGhcpKey",
|
|
83
|
+
ADMIN_CANCEL_EDIT_GHCP_KEY: "adminCancelEditGhcpKey",
|
|
84
|
+
ADMIN_SAVE_GHCP_KEY: "adminSaveGhcpKey",
|
|
85
|
+
ADMIN_CLEAR_GHCP_KEY: "adminClearGhcpKey",
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export function cycleValue(values, current, delta) {
|
|
89
|
+
const index = values.indexOf(current);
|
|
90
|
+
const safeIndex = index === -1 ? 0 : index;
|
|
91
|
+
const nextIndex = (safeIndex + delta + values.length) % values.length;
|
|
92
|
+
return values[nextIndex];
|
|
93
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
export function computeContextPercent(contextUsage) {
|
|
2
|
+
const utilization = typeof contextUsage?.utilization === "number"
|
|
3
|
+
? contextUsage.utilization
|
|
4
|
+
: (typeof contextUsage?.tokenLimit === "number"
|
|
5
|
+
&& contextUsage.tokenLimit > 0
|
|
6
|
+
&& typeof contextUsage?.currentTokens === "number"
|
|
7
|
+
? contextUsage.currentTokens / contextUsage.tokenLimit
|
|
8
|
+
: null);
|
|
9
|
+
|
|
10
|
+
if (typeof utilization !== "number" || !Number.isFinite(utilization)) return null;
|
|
11
|
+
return Math.max(0, Math.min(100, Math.round(utilization * 100)));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function formatTokenCount(value) {
|
|
15
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return "?";
|
|
16
|
+
if (value >= 1_000_000) {
|
|
17
|
+
const compact = value >= 10_000_000
|
|
18
|
+
? Math.round(value / 1_000_000)
|
|
19
|
+
: Math.round(value / 100_000) / 10;
|
|
20
|
+
return `${compact}`.replace(/\.0$/, "") + "m";
|
|
21
|
+
}
|
|
22
|
+
if (value >= 1_000) {
|
|
23
|
+
const compact = value >= 100_000
|
|
24
|
+
? Math.round(value / 1_000)
|
|
25
|
+
: Math.round(value / 100) / 10;
|
|
26
|
+
return `${compact}`.replace(/\.0$/, "") + "k";
|
|
27
|
+
}
|
|
28
|
+
return String(Math.round(value));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getContextMeterColor(contextUsage) {
|
|
32
|
+
const percent = computeContextPercent(contextUsage);
|
|
33
|
+
if (percent == null) return "gray";
|
|
34
|
+
if (percent >= 85) return "red";
|
|
35
|
+
if (percent >= 70) return "yellow";
|
|
36
|
+
return "gray";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getContextHeaderBadge(contextUsage) {
|
|
40
|
+
if (!contextUsage) return null;
|
|
41
|
+
if (!(typeof contextUsage.tokenLimit === "number"
|
|
42
|
+
&& contextUsage.tokenLimit > 0
|
|
43
|
+
&& typeof contextUsage.currentTokens === "number"
|
|
44
|
+
&& contextUsage.currentTokens >= 0)) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const percent = computeContextPercent(contextUsage);
|
|
49
|
+
if (percent == null) return null;
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
text: `ctx ${formatTokenCount(contextUsage.currentTokens)}/${formatTokenCount(contextUsage.tokenLimit)} ${percent}%`,
|
|
53
|
+
color: getContextMeterColor(contextUsage),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function getContextListBadge(contextUsage) {
|
|
58
|
+
if (!contextUsage) return null;
|
|
59
|
+
|
|
60
|
+
const compaction = contextUsage.compaction;
|
|
61
|
+
if (compaction?.state === "running") {
|
|
62
|
+
return { text: "[compact]", color: "magenta" };
|
|
63
|
+
}
|
|
64
|
+
if (compaction?.state === "failed") {
|
|
65
|
+
return { text: "[compact !]", color: "red" };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const percent = computeContextPercent(contextUsage);
|
|
69
|
+
if (percent == null || percent < 70) return null;
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
text: `[ctx ${percent}%]`,
|
|
73
|
+
color: percent >= 85 ? "red" : "yellow",
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function getContextCompactionBadge(contextUsage) {
|
|
78
|
+
const compaction = contextUsage?.compaction;
|
|
79
|
+
if (!compaction || typeof compaction !== "object") return null;
|
|
80
|
+
|
|
81
|
+
if (compaction.state === "running") {
|
|
82
|
+
return { text: "[compacting]", color: "magenta" };
|
|
83
|
+
}
|
|
84
|
+
if (compaction.state === "failed") {
|
|
85
|
+
return { text: "[compact failed]", color: "red" };
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function cloneContextUsageSnapshot(contextUsage) {
|
|
91
|
+
if (!contextUsage || typeof contextUsage !== "object") return null;
|
|
92
|
+
return {
|
|
93
|
+
...contextUsage,
|
|
94
|
+
...(contextUsage.compaction && typeof contextUsage.compaction === "object"
|
|
95
|
+
? { compaction: { ...contextUsage.compaction } }
|
|
96
|
+
: {}),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function normalizeUsageTimestamp(value) {
|
|
101
|
+
if (value instanceof Date) return value.getTime();
|
|
102
|
+
const numeric = new Date(value).getTime();
|
|
103
|
+
return Number.isFinite(numeric) ? numeric : Date.now();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function applySessionUsageEvent(contextUsage, eventType, data = {}, options = {}) {
|
|
107
|
+
if (!eventType) return null;
|
|
108
|
+
const current = cloneContextUsageSnapshot(contextUsage) || {};
|
|
109
|
+
const timestamp = normalizeUsageTimestamp(options.timestamp);
|
|
110
|
+
|
|
111
|
+
if (eventType === "session.usage_info") {
|
|
112
|
+
if (typeof data.tokenLimit !== "number"
|
|
113
|
+
|| typeof data.currentTokens !== "number"
|
|
114
|
+
|| typeof data.messagesLength !== "number") {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
current.tokenLimit = data.tokenLimit;
|
|
118
|
+
current.currentTokens = data.currentTokens;
|
|
119
|
+
current.messagesLength = data.messagesLength;
|
|
120
|
+
current.utilization = data.tokenLimit > 0 ? data.currentTokens / data.tokenLimit : 0;
|
|
121
|
+
if (typeof data.systemTokens === "number") current.systemTokens = data.systemTokens;
|
|
122
|
+
if (typeof data.conversationTokens === "number") current.conversationTokens = data.conversationTokens;
|
|
123
|
+
if (typeof data.toolDefinitionsTokens === "number") current.toolDefinitionsTokens = data.toolDefinitionsTokens;
|
|
124
|
+
if (typeof data.isInitial === "boolean") current.isInitial = data.isInitial;
|
|
125
|
+
return current;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (eventType === "assistant.usage") {
|
|
129
|
+
if (typeof data.inputTokens === "number") current.lastInputTokens = data.inputTokens;
|
|
130
|
+
if (typeof data.outputTokens === "number") current.lastOutputTokens = data.outputTokens;
|
|
131
|
+
if (typeof data.cacheReadTokens === "number") current.lastCacheReadTokens = data.cacheReadTokens;
|
|
132
|
+
if (typeof data.cacheWriteTokens === "number") current.lastCacheWriteTokens = data.cacheWriteTokens;
|
|
133
|
+
return current;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (eventType === "session.compaction_start") {
|
|
137
|
+
current.compaction = {
|
|
138
|
+
...(current.compaction || {}),
|
|
139
|
+
state: "running",
|
|
140
|
+
startedAt: timestamp,
|
|
141
|
+
completedAt: undefined,
|
|
142
|
+
error: undefined,
|
|
143
|
+
};
|
|
144
|
+
return current;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (eventType === "session.compaction_complete") {
|
|
148
|
+
current.compaction = {
|
|
149
|
+
...(current.compaction || {}),
|
|
150
|
+
state: data.success === false ? "failed" : "succeeded",
|
|
151
|
+
completedAt: timestamp,
|
|
152
|
+
error: typeof data.error === "string" ? data.error : undefined,
|
|
153
|
+
preCompactionTokens: typeof data.preCompactionTokens === "number" ? data.preCompactionTokens : undefined,
|
|
154
|
+
postCompactionTokens: typeof data.postCompactionTokens === "number" ? data.postCompactionTokens : undefined,
|
|
155
|
+
preCompactionMessagesLength: typeof data.preCompactionMessagesLength === "number" ? data.preCompactionMessagesLength : undefined,
|
|
156
|
+
messagesRemoved: typeof data.messagesRemoved === "number" ? data.messagesRemoved : undefined,
|
|
157
|
+
tokensRemoved: typeof data.tokensRemoved === "number" ? data.tokensRemoved : undefined,
|
|
158
|
+
systemTokens: typeof data.systemTokens === "number" ? data.systemTokens : undefined,
|
|
159
|
+
conversationTokens: typeof data.conversationTokens === "number" ? data.conversationTokens : undefined,
|
|
160
|
+
toolDefinitionsTokens: typeof data.toolDefinitionsTokens === "number" ? data.toolDefinitionsTokens : undefined,
|
|
161
|
+
inputTokens: typeof data.compactionTokensUsed?.input === "number" ? data.compactionTokensUsed.input : undefined,
|
|
162
|
+
outputTokens: typeof data.compactionTokensUsed?.output === "number" ? data.compactionTokensUsed.output : undefined,
|
|
163
|
+
cachedInputTokens: typeof data.compactionTokensUsed?.cachedInput === "number" ? data.compactionTokensUsed.cachedInput : undefined,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
if (typeof data.postCompactionTokens === "number" && typeof current.tokenLimit === "number" && current.tokenLimit > 0) {
|
|
167
|
+
current.currentTokens = data.postCompactionTokens;
|
|
168
|
+
current.utilization = data.postCompactionTokens / current.tokenLimit;
|
|
169
|
+
}
|
|
170
|
+
if (typeof data.preCompactionMessagesLength === "number" && typeof data.messagesRemoved === "number") {
|
|
171
|
+
current.messagesLength = Math.max(0, data.preCompactionMessagesLength - data.messagesRemoved);
|
|
172
|
+
}
|
|
173
|
+
if (typeof data.systemTokens === "number") current.systemTokens = data.systemTokens;
|
|
174
|
+
if (typeof data.conversationTokens === "number") current.conversationTokens = data.conversationTokens;
|
|
175
|
+
if (typeof data.toolDefinitionsTokens === "number") current.toolDefinitionsTokens = data.toolDefinitionsTokens;
|
|
176
|
+
return current;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function formatCompactionActivityRuns(timestamp, eventType, data = {}) {
|
|
183
|
+
const prefix = timestamp ? [{ text: `[${timestamp}] `, color: "white" }] : [];
|
|
184
|
+
|
|
185
|
+
if (eventType === "session.compaction_start") {
|
|
186
|
+
return [
|
|
187
|
+
...prefix,
|
|
188
|
+
{ text: "[compaction]", color: "magenta" },
|
|
189
|
+
{ text: " started", color: "white" },
|
|
190
|
+
];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (data.success === false) {
|
|
194
|
+
return [
|
|
195
|
+
...prefix,
|
|
196
|
+
{ text: "[compaction]", color: "red" },
|
|
197
|
+
{ text: " failed", color: "white" },
|
|
198
|
+
...(typeof data.error === "string" && data.error
|
|
199
|
+
? [{ text: `: ${data.error}`, color: "red" }]
|
|
200
|
+
: []),
|
|
201
|
+
];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return [
|
|
205
|
+
...prefix,
|
|
206
|
+
{ text: "[compaction]", color: "magenta" },
|
|
207
|
+
{ text: " complete", color: "white" },
|
|
208
|
+
...(typeof data.tokensRemoved === "number"
|
|
209
|
+
? [{ text: ` freed ${formatTokenCount(data.tokensRemoved)}`, color: "gray" }]
|
|
210
|
+
: []),
|
|
211
|
+
];
|
|
212
|
+
}
|