@timurproko/a1 0.1.8-dev.299 → 0.1.8-dev.322
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 +16 -4
- package/bin/guardian.js +4 -3
- package/bin/ui.js +4 -2
- package/bin/update-recovery.js +3 -1
- package/dist/composition/owned-ui.js +2 -1
- package/dist/features/launch/profile-paths.d.ts +2 -0
- package/dist/features/launch/profile-paths.js +11 -0
- package/dist/foundation/launch-context/index.d.ts +29 -0
- package/dist/foundation/launch-context/index.js +83 -0
- package/dist/foundation/launch-guardian/main.js +4 -3
- package/dist/foundation/release/bootstrap.d.ts +1 -1
- package/dist/foundation/release/bootstrap.js +18 -12
- package/dist/foundation/release/cohort-state.d.ts +1 -0
- package/dist/foundation/release/cohort-state.js +4 -0
- package/dist/foundation/release/release-gc.js +2 -2
- package/dist/foundation/release/release-store.js +10 -3
- package/dist/foundation/release/release.d.ts +3 -1
- package/dist/foundation/release/release.js +7 -3
- package/dist/foundation/release/restart-certification.d.ts +1 -0
- package/dist/foundation/release/restart-certification.js +5 -0
- package/dist/foundation/release/update-recovery.d.ts +2 -0
- package/dist/foundation/release/update-recovery.js +4 -0
- package/dist/foundation/release/update.js +5 -0
- package/dist/foundation/release/warmup.d.ts +1 -1
- package/dist/foundation/release/warmup.js +5 -4
- package/dist/foundation/startup/startup-runtime.js +13 -11
- package/dist/foundation/supervision/main.js +8 -7
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +1 -0
- package/dist/integrations/pi/components/owned-editor-ux.js +54 -3
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +3 -0
- package/dist/integrations/pi/components/shell-presenters-info.d.ts +9 -2
- package/dist/integrations/pi/components/shell-presenters-info.js +35 -4
- package/dist/integrations/pi/components/shell-selectors-dialogs.d.ts +3 -2
- package/dist/integrations/pi/components/shell-selectors-dialogs.js +8 -5
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +9 -2
- package/dist/integrations/pi/components/upstream/assets/earendil-image.json +12 -0
- package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +25 -2
- package/dist/integrations/pi/engine/adapter.d.ts +13 -1
- package/dist/integrations/pi/engine/adapter.js +86 -25
- package/dist/integrations/pi/engine/changelog.d.ts +2 -0
- package/dist/integrations/pi/engine/changelog.js +63 -0
- package/dist/integrations/pi/engine/resources/changelog.json +12 -0
- package/dist/integrations/pi/engine/workflow-controllers.d.ts +1 -1
- package/dist/integrations/pi/engine/workflows.d.ts +1 -1
- package/dist/integrations/pi/session-ui/prompt-chips.d.ts +3 -1
- package/dist/integrations/pi/session-ui/prompt-chips.js +37 -13
- package/dist/integrations/pi/session-ui/session-shell-root.d.ts +25 -0
- package/dist/integrations/pi/session-ui/session-shell-root.js +65 -74
- package/dist/integrations/pi/session-ui/session-shell.d.ts +1 -1
- package/dist/integrations/pi/session-ui/session-shell.js +58 -7
- package/dist/integrations/pi/session-ui/session-viewport-controller.d.ts +5 -0
- package/dist/integrations/pi/session-ui/session-viewport-controller.js +21 -15
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +7 -0
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +7 -6
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.exe +0 -0
- package/dist/product-identity.d.ts +1 -1
- package/dist/product-identity.js +2 -35
- package/dist/product-identity.json +0 -22
- package/dist/ui/components/transcript-viewport.d.ts +8 -0
- package/dist/ui/components/transcript-viewport.js +23 -2
- package/dist/ui/components/visible-hyperlinks.d.ts +7 -0
- package/dist/ui/components/visible-hyperlinks.js +1 -0
- package/dist/ui/settings/declarations.js +1 -0
- package/dist/ui/settings/sections.js +11 -1
- package/docs/architecture/boundaries.md +2 -1
- package/docs/architecture/internal-naming.md +89 -0
- package/docs/architecture/resource-and-data-policy.md +9 -4
- package/docs/architecture/toolchain.md +15 -1
- package/docs/features/prompt-history.md +39 -11
- package/package.json +8 -3
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
// Mechanically adapted from Pi commit 914cf14
|
|
2
2
|
// packages/coding-agent/src/modes/interactive/components/earendil-announcement.ts (MIT).
|
|
3
|
-
// Local modifications: remap private imports to public package-root/owned theme boundaries
|
|
4
|
-
import {
|
|
3
|
+
// Local modifications: remap private imports to public package-root/owned theme boundaries; load the verified owned copy of the image lazily.
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
import { Container, Image, Spacer, Text } from "#pi-tui";
|
|
5
6
|
import { DynamicBorder } from "@earendil-works/pi-coding-agent";
|
|
6
7
|
import { piTheme } from "../theme/theme.js";
|
|
7
8
|
const BLOG_URL = "https://mariozechner.at/posts/2026-04-08-ive-sold-out/";
|
|
9
|
+
const IMAGE_FILENAME = "clankolas.png";
|
|
10
|
+
let cachedImageBase64;
|
|
11
|
+
let attemptedImageLoad = false;
|
|
12
|
+
function loadImageBase64() {
|
|
13
|
+
if (attemptedImageLoad)
|
|
14
|
+
return cachedImageBase64;
|
|
15
|
+
attemptedImageLoad = true;
|
|
16
|
+
try {
|
|
17
|
+
const resource = JSON.parse(readFileSync(new URL("../assets/earendil-image.json", import.meta.url), "utf8"));
|
|
18
|
+
if (resource && typeof resource === "object" && "data" in resource && typeof resource.data === "string")
|
|
19
|
+
cachedImageBase64 = resource.data;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
cachedImageBase64 = undefined;
|
|
23
|
+
}
|
|
24
|
+
return cachedImageBase64;
|
|
25
|
+
}
|
|
8
26
|
export class EarendilAnnouncementComponent extends Container {
|
|
9
27
|
constructor() {
|
|
10
28
|
super();
|
|
@@ -14,6 +32,11 @@ export class EarendilAnnouncementComponent extends Container {
|
|
|
14
32
|
this.addChild(new Text(piTheme().fg("muted", "Read the blog post:"), 1, 0));
|
|
15
33
|
this.addChild(new Text(piTheme().fg("mdLink", BLOG_URL), 1, 0));
|
|
16
34
|
this.addChild(new Spacer(1));
|
|
35
|
+
const imageBase64 = loadImageBase64();
|
|
36
|
+
if (imageBase64) {
|
|
37
|
+
this.addChild(new Image(imageBase64, "image/png", { fallbackColor: text => piTheme().fg("muted", text) }, { maxWidthCells: 56, filename: IMAGE_FILENAME }));
|
|
38
|
+
this.addChild(new Spacer(1));
|
|
39
|
+
}
|
|
17
40
|
this.addChild(new DynamicBorder((text) => piTheme().fg("accent", text)));
|
|
18
41
|
}
|
|
19
42
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PiSessionForkPrompt, PiSessionSelection } from "./session-selection.js";
|
|
2
|
-
import { type AgentSessionRuntime, type AgentSessionServices, type SessionInfo } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type AgentSession, type AgentSessionRuntime, type AgentSessionServices, type SessionInfo } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import { OWNED_UI_EXTENSION_CONTRACT_VERSION, OWNED_UI_EXTENSION_RENDER_CALLBACKS, OWNED_UI_EXTENSION_UI_CALLBACKS, OWNED_UI_EXTENSION_UI_PROPERTIES, type OwnedUiCommand, type OwnedUiCommandOutcome, type OwnedUiImageAttachment, type OwnedUiPromptSuggestionGeneratorPort, type OwnedUiPromptSuggestionRequest, type OwnedUiPromptSuggestionResult, type OwnedUiEvent, type OwnedUiSessionViewModel, type OwnedUiSnapshot } from "../../../contracts/owned-ui/index.js";
|
|
4
4
|
import { type PiAuthenticationProviderOption, type PiBashWorkflowResult, type PiPinnedSettingsCallback, type PiPinnedSettingsSnapshot, type PiWorkflowAutocompleteCommand, type PiWorkflowHost, type PiWorkflowInteractionHost, type PiWorkflowOption, type PiWorkflowRequest, type PiWorkflowResult } from "./workflows.js";
|
|
5
5
|
import { PiSettingsIntegration } from "./settings-integration.js";
|
|
@@ -176,16 +176,28 @@ export declare class PiEngineAdapter implements OwnedUiPromptSuggestionGenerator
|
|
|
176
176
|
readonly title: string;
|
|
177
177
|
readonly options: readonly PiWorkflowOption[];
|
|
178
178
|
};
|
|
179
|
+
pinnedAmbientAuthentication(selection: string): {
|
|
180
|
+
readonly providerId: string;
|
|
181
|
+
readonly providerName: string;
|
|
182
|
+
readonly title: string;
|
|
183
|
+
readonly message: string;
|
|
184
|
+
} | null;
|
|
179
185
|
pinnedLogoutOptions(): Promise<readonly PiAuthenticationProviderOption[]>;
|
|
180
186
|
pinnedForkOptions(): readonly PiWorkflowOption[];
|
|
181
187
|
pinnedTreeSelectorContext(): PiTreeSelectorContext;
|
|
182
188
|
pinnedSettingsSnapshot(): PiPinnedSettingsSnapshot;
|
|
183
189
|
pinnedMessageRenderer(customType: string): unknown;
|
|
190
|
+
pinnedShortcutDescriptions(bindings: Parameters<AgentSession["extensionRunner"]["getShortcuts"]>[0]): readonly {
|
|
191
|
+
readonly key: string;
|
|
192
|
+
readonly description: string;
|
|
193
|
+
}[];
|
|
184
194
|
pinnedToolDefinition(toolName: string): unknown;
|
|
195
|
+
copyWorkflowText(text: string): Promise<void>;
|
|
185
196
|
clearQueuedWorkflows(): readonly string[];
|
|
186
197
|
abortBashWorkflow(): void;
|
|
187
198
|
executeBashWorkflow(command: string, excludeFromContext: boolean): Promise<PiBashWorkflowResult>;
|
|
188
199
|
applyPinnedSettingValue(callback: PiPinnedSettingsCallback, value: unknown): Promise<PiWorkflowResult>;
|
|
200
|
+
reloadBlockedResult(): PiWorkflowResult | null;
|
|
189
201
|
executeWorkflow(request: PiWorkflowRequest): Promise<PiWorkflowResult>;
|
|
190
202
|
view(): OwnedUiSessionViewModel;
|
|
191
203
|
snapshot(): OwnedUiSnapshot;
|
|
@@ -7,6 +7,7 @@ import { dirname, join, resolve } from "node:path";
|
|
|
7
7
|
import { promisify } from "node:util";
|
|
8
8
|
import { PRODUCT_IDENTITY } from "../../../product-identity.js";
|
|
9
9
|
import { configureOwnedHttpDispatcher } from "./http-dispatcher.js";
|
|
10
|
+
import { readPinnedCommandChangelog } from "./changelog.js";
|
|
10
11
|
import { copyToClipboard, CredentialSynchronizationError, DefaultPackageManager, getAgentDir, ProjectTrustStore, SessionManager, VERSION, } from "@earendil-works/pi-coding-agent";
|
|
11
12
|
import { OWNED_UI_EXTENSION_CONTRACT_VERSION, OWNED_UI_EXTENSION_RENDER_CALLBACKS, OWNED_UI_EXTENSION_UI_CALLBACKS, OWNED_UI_EXTENSION_UI_PROPERTIES, CONTEXTUAL_PROMPT_SUGGESTION_INSTRUCTION, assertOwnedUiCommand, assertOwnedUiExtensionUiPort, assertOwnedUiPromptSuggestionRequest, assertOwnedUiPromptSuggestionResult, assertOwnedUiSnapshot, normalizePromptSuggestionCandidate, } from "../../../contracts/owned-ui/index.js";
|
|
12
13
|
import { PINNED_PI_SETTINGS_CALLBACKS, PINNED_PI_WORKFLOW_COMMAND_NAMES, } from "./workflows.js";
|
|
@@ -308,14 +309,22 @@ export class PiEngineAdapter {
|
|
|
308
309
|
if (!settingsManager || typeof settingsManager.getLastChangelogVersion !== "function"
|
|
309
310
|
|| typeof settingsManager.setLastChangelogVersion !== "function")
|
|
310
311
|
return;
|
|
311
|
-
if (
|
|
312
|
+
if ((this.#session?.messages.length ?? 0) > 0)
|
|
312
313
|
return;
|
|
313
|
-
const
|
|
314
|
+
const lastVersion = settingsManager.getLastChangelogVersion();
|
|
315
|
+
if (lastVersion === VERSION)
|
|
316
|
+
return;
|
|
317
|
+
if (!lastVersion) {
|
|
318
|
+
settingsManager.setLastChangelogVersion(VERSION);
|
|
319
|
+
await settingsManager.flush();
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const markdown = await this.#workflowHost.readChangelog(lastVersion).catch(() => "");
|
|
314
323
|
if (markdown.trim().length > 0) {
|
|
315
324
|
this.#addDiagnostic("info", settingsManager.getCollapseChangelog() ? "changelog-collapsed" : "changelog-expanded", markdown, true);
|
|
325
|
+
settingsManager.setLastChangelogVersion(VERSION);
|
|
326
|
+
await settingsManager.flush();
|
|
316
327
|
}
|
|
317
|
-
settingsManager.setLastChangelogVersion(VERSION);
|
|
318
|
-
await settingsManager.flush();
|
|
319
328
|
}
|
|
320
329
|
async #announcePackageUpdates(settingsManager) {
|
|
321
330
|
if (process.env.PI_OFFLINE)
|
|
@@ -877,6 +886,17 @@ export class PiEngineAdapter {
|
|
|
877
886
|
}));
|
|
878
887
|
return { title: `Select authentication method for ${providerName}:`, options };
|
|
879
888
|
}
|
|
889
|
+
pinnedAmbientAuthentication(selection) {
|
|
890
|
+
if (!selection.startsWith("api_key:"))
|
|
891
|
+
return null;
|
|
892
|
+
const providerId = selection.slice("api_key:".length);
|
|
893
|
+
const provider = this.#runtime?.services.modelRuntime.getProvider?.(providerId);
|
|
894
|
+
const method = dynamicObject(dynamicObject(provider, "auth"), "apiKey");
|
|
895
|
+
if (!method || typeof method.login === "function")
|
|
896
|
+
return null;
|
|
897
|
+
const providerName = stringProperty(provider, "name") ?? providerId;
|
|
898
|
+
return { providerId, providerName, title: `${providerName} setup`, message: `${stringProperty(method, "name") ?? "Authentication"} is configured outside pi.` };
|
|
899
|
+
}
|
|
880
900
|
pinnedLogoutOptions() {
|
|
881
901
|
return this.#logoutOptions();
|
|
882
902
|
}
|
|
@@ -957,9 +977,16 @@ export class PiEngineAdapter {
|
|
|
957
977
|
pinnedMessageRenderer(customType) {
|
|
958
978
|
return this.#requireWorkflowSession().extensionRunner?.getMessageRenderer?.(customType);
|
|
959
979
|
}
|
|
980
|
+
pinnedShortcutDescriptions(bindings) {
|
|
981
|
+
const shortcuts = this.#requireWorkflowSession().extensionRunner?.getShortcuts?.(bindings);
|
|
982
|
+
return shortcuts === undefined ? [] : [...shortcuts].map(([key, shortcut]) => ({ key, description: shortcut.description ?? shortcut.extensionPath }));
|
|
983
|
+
}
|
|
960
984
|
pinnedToolDefinition(toolName) {
|
|
961
985
|
return this.#requireWorkflowSession().extensionRunner?.getToolDefinition?.(toolName);
|
|
962
986
|
}
|
|
987
|
+
async copyWorkflowText(text) {
|
|
988
|
+
await this.#workflowHost.copyText(text);
|
|
989
|
+
}
|
|
963
990
|
clearQueuedWorkflows() {
|
|
964
991
|
const session = this.#requireWorkflowSession();
|
|
965
992
|
const result = session.clearQueue?.();
|
|
@@ -992,6 +1019,14 @@ export class PiEngineAdapter {
|
|
|
992
1019
|
return workflowResult("settings", "failed", error instanceof Error ? error.message : String(error));
|
|
993
1020
|
}
|
|
994
1021
|
}
|
|
1022
|
+
reloadBlockedResult() {
|
|
1023
|
+
const session = this.#requireWorkflowSession();
|
|
1024
|
+
if (session.isStreaming)
|
|
1025
|
+
return workflowResult("reload", "failed", "Wait for the current response to finish before reloading.", undefined, "warning");
|
|
1026
|
+
if (session.isCompacting)
|
|
1027
|
+
return workflowResult("reload", "failed", "Wait for compaction to finish before reloading.", undefined, "warning");
|
|
1028
|
+
return null;
|
|
1029
|
+
}
|
|
995
1030
|
async executeWorkflow(request) {
|
|
996
1031
|
try {
|
|
997
1032
|
return await this.#performWorkflow(request);
|
|
@@ -999,7 +1034,7 @@ export class PiEngineAdapter {
|
|
|
999
1034
|
catch (error) {
|
|
1000
1035
|
const message = error instanceof Error ? error.message : String(error);
|
|
1001
1036
|
const contextualMessage = request.command === "export"
|
|
1002
|
-
? `Failed to export session: ${
|
|
1037
|
+
? `Failed to export session: ${errorMessage(error, "Unknown error")}`
|
|
1003
1038
|
: request.command === "import"
|
|
1004
1039
|
? `Failed to import session: ${message}`
|
|
1005
1040
|
: request.command === "new"
|
|
@@ -1133,8 +1168,18 @@ export class PiEngineAdapter {
|
|
|
1133
1168
|
: [...(runtime.services.modelRuntime.getAvailableSnapshot?.() ?? [])];
|
|
1134
1169
|
let model = findExactWorkflowModel(reference, availableModels);
|
|
1135
1170
|
const messages = [];
|
|
1171
|
+
const generation = this.#sessionGeneration;
|
|
1172
|
+
const current = () => !this.#disposed && generation === this.#sessionGeneration;
|
|
1173
|
+
const publish = (message) => {
|
|
1174
|
+
if (!current())
|
|
1175
|
+
return;
|
|
1176
|
+
if (this.#workflowInteraction.publish)
|
|
1177
|
+
this.#workflowInteraction.publish(message);
|
|
1178
|
+
else
|
|
1179
|
+
messages.push(message);
|
|
1180
|
+
};
|
|
1136
1181
|
if (model === undefined && scopedModels.length === 0) {
|
|
1137
|
-
|
|
1182
|
+
publish({ kind: "status", message: "Refreshing model catalogs…" });
|
|
1138
1183
|
const controller = new AbortController();
|
|
1139
1184
|
let timedOut = false;
|
|
1140
1185
|
const timeout = setTimeout(() => {
|
|
@@ -1144,14 +1189,14 @@ export class PiEngineAdapter {
|
|
|
1144
1189
|
try {
|
|
1145
1190
|
const refreshed = await runtime.services.modelRuntime.refresh?.({ signal: controller.signal });
|
|
1146
1191
|
if (isRecord(refreshed) && refreshed.aborted === true && timedOut) {
|
|
1147
|
-
|
|
1192
|
+
publish({ kind: "warning", message: "Model refresh timed out; searching cached models." });
|
|
1148
1193
|
}
|
|
1149
1194
|
else if (isRecord(refreshed) && refreshed.errors instanceof Map && refreshed.errors.size > 0) {
|
|
1150
|
-
|
|
1195
|
+
publish({ kind: "warning", message: `Could not refresh ${[...refreshed.errors.keys()].join(", ")}; searching cached models.` });
|
|
1151
1196
|
}
|
|
1152
1197
|
}
|
|
1153
1198
|
catch (error) {
|
|
1154
|
-
|
|
1199
|
+
publish({
|
|
1155
1200
|
kind: "warning",
|
|
1156
1201
|
message: timedOut
|
|
1157
1202
|
? "Model refresh timed out; searching cached models."
|
|
@@ -1164,13 +1209,25 @@ export class PiEngineAdapter {
|
|
|
1164
1209
|
availableModels = [...(runtime.services.modelRuntime.getAvailableSnapshot?.() ?? [])];
|
|
1165
1210
|
model = findExactWorkflowModel(reference, availableModels);
|
|
1166
1211
|
}
|
|
1212
|
+
if (!current())
|
|
1213
|
+
return workflowResult(request.command, "cancelled", "Model selection cancelled", undefined, "silent");
|
|
1167
1214
|
if (model === undefined) {
|
|
1168
1215
|
return {
|
|
1169
1216
|
...workflowResult(request.command, "requires-selection", "Select a model", reference, "silent"),
|
|
1170
1217
|
...(messages.length === 0 ? {} : { messages: Object.freeze(messages) }),
|
|
1171
1218
|
};
|
|
1172
1219
|
}
|
|
1173
|
-
|
|
1220
|
+
try {
|
|
1221
|
+
await session.setModel(model);
|
|
1222
|
+
}
|
|
1223
|
+
catch (error) {
|
|
1224
|
+
if (!current())
|
|
1225
|
+
return workflowResult(request.command, "cancelled", "Model selection cancelled", undefined, "silent");
|
|
1226
|
+
const failure = { kind: "error", message: error instanceof Error ? error.message : String(error) };
|
|
1227
|
+
return workflowResult(request.command, "failed", failure.message, undefined, "error", messages.length === 0 ? undefined : [...messages, failure]);
|
|
1228
|
+
}
|
|
1229
|
+
if (!current())
|
|
1230
|
+
return workflowResult(request.command, "cancelled", "Model selection cancelled", undefined, "silent");
|
|
1174
1231
|
const providerId = stringProperty(model, "provider") ?? "unknown";
|
|
1175
1232
|
const modelId = stringProperty(model, "id") ?? reference;
|
|
1176
1233
|
this.#activeModel = { providerId, modelId, displayName: stringProperty(model, "name") ?? modelId };
|
|
@@ -1226,9 +1283,8 @@ export class PiEngineAdapter {
|
|
|
1226
1283
|
const signal = request.signal;
|
|
1227
1284
|
if (signal?.aborted)
|
|
1228
1285
|
return workflowResult(request.command, "cancelled", "Share cancelled", undefined, "status");
|
|
1229
|
-
let auth;
|
|
1230
1286
|
try {
|
|
1231
|
-
|
|
1287
|
+
await this.#workflowHost.runCommand("gh", ["auth", "status"], signal === undefined ? undefined : { signal });
|
|
1232
1288
|
}
|
|
1233
1289
|
catch (error) {
|
|
1234
1290
|
if (signal?.aborted || isAbortError(error))
|
|
@@ -1238,9 +1294,6 @@ export class PiEngineAdapter {
|
|
|
1238
1294
|
: "GitHub CLI is not logged in. Run 'gh auth login' first.";
|
|
1239
1295
|
return workflowResult(request.command, "failed", message);
|
|
1240
1296
|
}
|
|
1241
|
-
if (auth.stderr && !auth.stdout) {
|
|
1242
|
-
return workflowResult(request.command, "failed", "GitHub CLI is not logged in. Run 'gh auth login' first.");
|
|
1243
|
-
}
|
|
1244
1297
|
const temporary = join(tmpdir(), `${PRODUCT_IDENTITY.filesystem.temporaryPrefix}pi-session-${process.pid}.html`);
|
|
1245
1298
|
try {
|
|
1246
1299
|
try {
|
|
@@ -1258,13 +1311,10 @@ export class PiEngineAdapter {
|
|
|
1258
1311
|
catch (error) {
|
|
1259
1312
|
if (signal?.aborted || isAbortError(error))
|
|
1260
1313
|
return workflowResult(request.command, "cancelled", "Share cancelled", undefined, "status");
|
|
1261
|
-
return workflowResult(request.command, "failed", `Failed to create gist: ${
|
|
1314
|
+
return workflowResult(request.command, "failed", `Failed to create gist: ${shareCommandFailureDetail(error)}`);
|
|
1262
1315
|
}
|
|
1263
1316
|
if (signal?.aborted)
|
|
1264
1317
|
return workflowResult(request.command, "cancelled", "Share cancelled", undefined, "status");
|
|
1265
|
-
if (gist.stderr && !gist.stdout) {
|
|
1266
|
-
return workflowResult(request.command, "failed", `Failed to create gist: ${gist.stderr.trim() || "Unknown error"}`);
|
|
1267
|
-
}
|
|
1268
1318
|
const gistUrl = gist.stdout.trim();
|
|
1269
1319
|
const gistId = gistUrl.split("/").at(-1);
|
|
1270
1320
|
if (!gistId)
|
|
@@ -1497,13 +1547,17 @@ export class PiEngineAdapter {
|
|
|
1497
1547
|
}
|
|
1498
1548
|
}
|
|
1499
1549
|
case "reload": {
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
return workflowResult(request.command, "failed", "Wait for compaction to finish before reloading.", undefined, "warning");
|
|
1550
|
+
const blocked = this.reloadBlockedResult();
|
|
1551
|
+
if (blocked)
|
|
1552
|
+
return blocked;
|
|
1504
1553
|
await requireCapability(session.reload, "reload").call(session);
|
|
1505
1554
|
await this.#bindExtensionUiToSession();
|
|
1506
|
-
|
|
1555
|
+
const message = "Reloaded keybindings, extensions, skills, prompts, themes, and context files";
|
|
1556
|
+
const modelError = runtime.services.modelRuntime.getError?.();
|
|
1557
|
+
return workflowResult(request.command, "completed", message, undefined, "status", modelError ? [
|
|
1558
|
+
{ kind: "error", message: `models.json error: ${modelError}` },
|
|
1559
|
+
{ kind: "status", message },
|
|
1560
|
+
] : undefined);
|
|
1507
1561
|
}
|
|
1508
1562
|
case "quit": {
|
|
1509
1563
|
await this.dispose();
|
|
@@ -2698,7 +2752,7 @@ function defaultWorkflowHost() {
|
|
|
2698
2752
|
const result = await execFileAsync(command, [...arguments_], { encoding: "utf8", signal: options?.signal });
|
|
2699
2753
|
return { stdout: result.stdout, stderr: result.stderr };
|
|
2700
2754
|
},
|
|
2701
|
-
readChangelog:
|
|
2755
|
+
readChangelog: readPinnedCommandChangelog,
|
|
2702
2756
|
};
|
|
2703
2757
|
}
|
|
2704
2758
|
function workflowLoginNotification(event) {
|
|
@@ -2757,6 +2811,13 @@ function workflowConfirmation(command, message, detail) {
|
|
|
2757
2811
|
function errorMessage(error, fallback) {
|
|
2758
2812
|
return error instanceof Error ? error.message : fallback;
|
|
2759
2813
|
}
|
|
2814
|
+
function shareCommandFailureDetail(error) {
|
|
2815
|
+
// Compatibility: Pi reports a failed child's stderr, not execFile's command wrapper.
|
|
2816
|
+
if (isRecord(error) && (typeof error.code === "number" || typeof error.signal === "string") && typeof error.stderr === "string") {
|
|
2817
|
+
return error.stderr.trim() || "Unknown error";
|
|
2818
|
+
}
|
|
2819
|
+
return errorMessage(error, "Unknown error");
|
|
2820
|
+
}
|
|
2760
2821
|
function commandMissing(error) {
|
|
2761
2822
|
return isRecord(error) && error.code === "ENOENT";
|
|
2762
2823
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { posix } from "node:path";
|
|
4
|
+
const REPOSITORY = "https://github.com/earendil-works/pi";
|
|
5
|
+
export async function readPinnedCommandChangelog(sinceVersion) {
|
|
6
|
+
const path = new URL("./resources/changelog.json", import.meta.url);
|
|
7
|
+
if (!existsSync(path))
|
|
8
|
+
return sinceVersion === undefined ? "No changelog entries found." : "";
|
|
9
|
+
try {
|
|
10
|
+
const resource = JSON.parse(await readFile(path, "utf8"));
|
|
11
|
+
if (!resource || typeof resource !== "object" || !("data" in resource) || typeof resource.data !== "string")
|
|
12
|
+
throw new TypeError("Invalid owned changelog resource");
|
|
13
|
+
const markdown = resource.data;
|
|
14
|
+
return formatPinnedCommandChangelog(markdown, sinceVersion);
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
// Compatibility: Pi's data-file parser reports a warning and supplies an empty changelog.
|
|
18
|
+
console.error(`Warning: Could not parse changelog: ${error}`);
|
|
19
|
+
return sinceVersion === undefined ? "No changelog entries found." : "";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function formatPinnedCommandChangelog(content, sinceVersion) {
|
|
23
|
+
// Provenance: behavior follows Pi 0.84.2 utils/changelog.ts at 914cf1472e715297caa30db4b9535d534a9eb718.
|
|
24
|
+
// Runtime reads only the owned data resource; maintenance tooling verifies its pinned provenance.
|
|
25
|
+
let releases = content.split(/(?=^## )/m).flatMap((section) => {
|
|
26
|
+
const version = /^##\s+\[?(\d+)\.(\d+)\.(\d+)\]?/.exec(section);
|
|
27
|
+
return version ? [{ version: version.slice(1).map(Number), markdown: section.trim() }] : [];
|
|
28
|
+
});
|
|
29
|
+
if (sinceVersion !== undefined) {
|
|
30
|
+
const previous = sinceVersion.split(".").map(value => Number(value) || 0);
|
|
31
|
+
releases = releases.filter(release => {
|
|
32
|
+
for (let index = 0; index < 3; index++) {
|
|
33
|
+
const difference = release.version[index] - (previous[index] ?? 0);
|
|
34
|
+
if (difference !== 0)
|
|
35
|
+
return difference > 0;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
else
|
|
41
|
+
releases.reverse();
|
|
42
|
+
if (releases.length === 0)
|
|
43
|
+
return sinceVersion === undefined ? "No changelog entries found." : "";
|
|
44
|
+
return releases.map(release => release.markdown.replace(/(!?\[[^\]\n]+\]\()([^\s)]+)((?:\s+[^)]*)?\))/g, (_match, prefix, target, suffix) => `${prefix}${releaseLink(target, `v${release.version.join(".")}`)}${suffix}`)).join("\n\n");
|
|
45
|
+
}
|
|
46
|
+
function releaseLink(target, tag) {
|
|
47
|
+
const canonical = target
|
|
48
|
+
.replace(/^https:\/\/github\.com\/(?:badlogic|earendil-works)\/pi-mono(?=\/|$)/, REPOSITORY)
|
|
49
|
+
.replace(/^(https:\/\/github\.com\/earendil-works\/pi\/(?:blob|tree)\/)(?:main|master)\//, `$1${tag}/`);
|
|
50
|
+
if (/^(?:#|\/\/|[a-z][a-z0-9+.-]*:)/i.test(canonical))
|
|
51
|
+
return canonical;
|
|
52
|
+
const boundary = canonical.search(/[?#]/);
|
|
53
|
+
const path = boundary === -1 ? canonical : canonical.slice(0, boundary);
|
|
54
|
+
const suffix = boundary === -1 ? "" : canonical.slice(boundary);
|
|
55
|
+
if (!path)
|
|
56
|
+
return canonical;
|
|
57
|
+
const normalized = path.replaceAll("\\", "/");
|
|
58
|
+
const repositoryPath = posix.normalize(normalized.startsWith("/") ? normalized.replace(/^\/+/, "") : posix.join("packages/coding-agent", normalized));
|
|
59
|
+
if (repositoryPath === "." || repositoryPath === ".." || repositoryPath.startsWith("../"))
|
|
60
|
+
return canonical;
|
|
61
|
+
const route = path.endsWith("/") || !posix.basename(repositoryPath).includes(".") ? "tree" : "blob";
|
|
62
|
+
return `${REPOSITORY}/${route}/${tag}/${encodeURI(repositoryPath)}${suffix}`;
|
|
63
|
+
}
|