@jmfederico/pi-web 1.202610.0 → 1.202610.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -134
- package/dist/cli.js +23 -4
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/index-DUsqiytv.js +4408 -0
- package/dist/client/assets/vendor-editor-core-C2PJX3uW.js +10 -0
- package/dist/client/assets/{vendor-editor-languages-DOCw3vc9.js → vendor-editor-languages-CS6n1J8n.js} +1 -1
- package/dist/client/index.html +4 -48
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/pi-packages/captains-log/dist/browser/index.js +34 -34
- package/dist/pi-packages/captains-log/package.json +5 -5
- package/dist/pi-packages/relays/extensions/index.js +60 -0
- package/dist/pi-packages/relays/package.json +10 -1
- package/dist/pi-packages/relays/pi-web-plugin.js +31 -1
- package/dist/pi-packages/relays/prompts/relay-worktree.md +6 -16
- package/dist/pi-packages/relays/prompts/relay.md +6 -33
- package/dist/pi-packages/relays/relayDispatch.js +67 -0
- package/dist/pi-packages/relays/relayIdentity.js +37 -0
- package/dist/pi-packages/relays/relaysPanelElement.js +37 -12
- package/dist/pi-packages/relays/skills/relay/SKILL.md +24 -68
- package/dist/pi-packages/relays/skills/relay-runner/SKILL.md +40 -219
- package/dist/pi-packages/workflows/browser/pi-web-plugin.js +33 -0
- package/dist/pi-packages/workflows/package.json +22 -0
- package/dist/pi-packages/workflows/prompts/worktree.md +12 -0
- package/dist/pi-web-plugins/files/browser/pi-web-plugin.js +53 -53
- package/dist/pi-web-plugins/git/browser/git-panel.js +0 -1
- package/dist/pi-web-plugins/git/server-plugin.js +11 -0
- package/dist/pi-web-plugins/git/workspace-discovery-notify.cjs +14 -0
- package/dist/pi-web-plugins/git/workspaceDiscoveryHooks.js +180 -0
- package/dist/pi-web-plugins/info/infoInternals.js +3 -3
- package/dist/pi-web-plugins/info/pi-web-plugin.js +1 -1
- package/dist/pi-web-plugins/mermaid/browser/mermaid-engine.js +1077 -414
- package/dist/pi-web-plugins/terminal/browser/pi-web-plugin.js +30 -30
- package/dist/pi-web-plugins/updates/pi-web-plugin.js +2 -2
- package/dist/pi-web-plugins/updates/updatesLogic.js +6 -5
- package/dist/piWebUpdate.js +405 -0
- package/dist/piWebUpdate.js.map +1 -0
- package/dist/piWebVersionReport.js +43 -1
- package/dist/piWebVersionReport.js.map +1 -1
- package/dist/plugin-api.d.ts +229 -8
- package/dist/server/activity/workspaceActivityService.js +4 -1
- package/dist/server/activity/workspaceActivityService.js.map +1 -1
- package/dist/server/app.js +5 -0
- package/dist/server/app.js.map +1 -1
- package/dist/server/configRoutes.js +6 -0
- package/dist/server/configRoutes.js.map +1 -1
- package/dist/server/knownAutoInstallPiPackages.js +6 -0
- package/dist/server/knownAutoInstallPiPackages.js.map +1 -1
- package/dist/server/navigationPreferenceRoutes.js +39 -0
- package/dist/server/navigationPreferenceRoutes.js.map +1 -0
- package/dist/server/piWebStatus.js +19 -25
- package/dist/server/piWebStatus.js.map +1 -1
- package/dist/server/plugins/projectPluginSettings.js +95 -0
- package/dist/server/plugins/projectPluginSettings.js.map +1 -0
- package/dist/server/plugins/serverPluginRuntime.js +26 -3
- package/dist/server/plugins/serverPluginRuntime.js.map +1 -1
- package/dist/server/sessiond/autoInstallPiPackages.js +3 -3
- package/dist/server/sessiond/workspaceCatalogRoutes.js +3 -1
- package/dist/server/sessiond/workspaceCatalogRoutes.js.map +1 -1
- package/dist/server/sessiond/workspaceNotificationRoutes.js +41 -0
- package/dist/server/sessiond/workspaceNotificationRoutes.js.map +1 -0
- package/dist/server/sessiond.js +40 -7
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/askUserDomain.js +232 -0
- package/dist/server/sessions/askUserDomain.js.map +1 -0
- package/dist/server/sessions/askUserTool.js +17 -17
- package/dist/server/sessions/askUserTool.js.map +1 -1
- package/dist/server/sessions/builtinExtensionFactories.js +13 -0
- package/dist/server/sessions/builtinExtensionFactories.js.map +1 -0
- package/dist/server/sessions/dockerEnvironmentFacts.js +8 -0
- package/dist/server/sessions/dockerEnvironmentFacts.js.map +1 -1
- package/dist/server/sessions/extensionSessionDispatch.js +76 -0
- package/dist/server/sessions/extensionSessionDispatch.js.map +1 -0
- package/dist/server/sessions/globalProviderPolicy.js +94 -18
- package/dist/server/sessions/globalProviderPolicy.js.map +1 -1
- package/dist/server/sessions/messageHistoryTarget.js +61 -0
- package/dist/server/sessions/messageHistoryTarget.js.map +1 -0
- package/dist/server/sessions/piSessionManagerGateway.js +23 -24
- package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
- package/dist/server/sessions/piSessionService.js +604 -266
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/sessionArchiveStore.js +5 -1
- package/dist/server/sessions/sessionArchiveStore.js.map +1 -1
- package/dist/server/sessions/sessionArchiveTree.js +2 -2
- package/dist/server/sessions/sessionArchiveTree.js.map +1 -1
- package/dist/server/sessions/sessionCommandService.js +9 -3
- package/dist/server/sessions/sessionCommandService.js.map +1 -1
- package/dist/server/sessions/sessionImageDimensions.js +31 -0
- package/dist/server/sessions/sessionImageDimensions.js.map +1 -0
- package/dist/server/sessions/sessionMediaIndex.js +7 -1
- package/dist/server/sessions/sessionMediaIndex.js.map +1 -1
- package/dist/server/sessions/sessionMetadata.js +51 -0
- package/dist/server/sessions/sessionMetadata.js.map +1 -0
- package/dist/server/sessions/sessionNameGenerator.js +8 -28
- package/dist/server/sessions/sessionNameGenerator.js.map +1 -1
- package/dist/server/sessions/sessionRoutes.js +10 -2
- package/dist/server/sessions/sessionRoutes.js.map +1 -1
- package/dist/server/sessions/sessionSummaryScanner.js +62 -5
- package/dist/server/sessions/sessionSummaryScanner.js.map +1 -1
- package/dist/server/sessions/spawnSessionTool.js +3 -1
- package/dist/server/sessions/spawnSessionTool.js.map +1 -1
- package/dist/server/sessions/spawnSubsessionTool.js +27 -17
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
- package/dist/server/sessions/subsessionCompletionDelivery.js +15 -0
- package/dist/server/sessions/subsessionCompletionDelivery.js.map +1 -0
- package/dist/server/sessions/subsessionCompletionInbox.js +134 -0
- package/dist/server/sessions/subsessionCompletionInbox.js.map +1 -0
- package/dist/server/sessions/subsessionProvenance.js +27 -0
- package/dist/server/sessions/subsessionProvenance.js.map +1 -0
- package/dist/server/sessions/transcriptMarkdown.js +48 -0
- package/dist/server/sessions/transcriptMarkdown.js.map +1 -0
- package/dist/server/sessions/transcriptMessages.js +3 -0
- package/dist/server/sessions/transcriptMessages.js.map +1 -1
- package/dist/server/status/machineStatusService.js +17 -5
- package/dist/server/status/machineStatusService.js.map +1 -1
- package/dist/server/storage/navigationPreferenceStore.js +137 -0
- package/dist/server/storage/navigationPreferenceStore.js.map +1 -0
- package/dist/server/workspaces/projectPiWebConfig.js +88 -6
- package/dist/server/workspaces/projectPiWebConfig.js.map +1 -1
- package/dist/server/workspaces/workspaceChangeNotifier.js +12 -0
- package/dist/server/workspaces/workspaceChangeNotifier.js.map +1 -0
- package/dist/server/workspaces/workspaceNotificationCommand.js +64 -0
- package/dist/server/workspaces/workspaceNotificationCommand.js.map +1 -0
- package/dist/server/workspaces/workspaceProviderRegistry.js +55 -0
- package/dist/server/workspaces/workspaceProviderRegistry.js.map +1 -1
- package/dist/server-plugin-api.d.ts +18 -0
- package/dist/server-plugin-api.js.map +1 -1
- package/dist/sessiond/sessionDaemonClient.js +3 -3
- package/dist/sessiond/sessionDaemonClient.js.map +1 -1
- package/dist/shared/apiTypes.js +2 -2
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/federatedRoutes.js +5 -0
- package/dist/shared/federatedRoutes.js.map +1 -1
- package/dist/shared/machineStatus.js +11 -2
- package/dist/shared/machineStatus.js.map +1 -1
- package/dist/shared/navigationPreferences.js +6 -0
- package/dist/shared/navigationPreferences.js.map +1 -0
- package/dist/shared/pluginApiTypes.d.ts +2 -0
- package/dist/shared/sessionMedia.js +6 -0
- package/dist/shared/sessionMedia.js.map +1 -1
- package/dist/shared/streamingBehavior.js +7 -0
- package/dist/shared/streamingBehavior.js.map +1 -0
- package/dist/shared/themePreference.js +23 -0
- package/dist/shared/themePreference.js.map +1 -0
- package/dist/workspaceNotificationCli.js +95 -0
- package/dist/workspaceNotificationCli.js.map +1 -0
- package/docs/config.md +100 -85
- package/docs/plugins.md +109 -30
- package/examples/session-bridge-plugin/README.md +1 -1
- package/examples/session-bridge-plugin/package.json +3 -3
- package/examples/workspace-provider-plugin/package.json +1 -1
- package/package.json +36 -36
- package/dist/client/assets/index-_1GwJfSs.js +0 -4176
- package/dist/client/assets/vendor-editor-core-YWog6mAR.js +0 -10
- package/dist/server/sessions/pendingAskStore.js +0 -275
- package/dist/server/sessions/pendingAskStore.js.map +0 -1
|
@@ -346,7 +346,6 @@ function createGitPanel(html, svg, controller) {
|
|
|
346
346
|
</svg>
|
|
347
347
|
`,
|
|
348
348
|
order: 20,
|
|
349
|
-
routeAliases: ["git", "core:workspace.git"],
|
|
350
349
|
visible: (context) => controller.isOwnedWorkspace(context.workspace),
|
|
351
350
|
onInvalidate: (context) => controller.invalidate(context),
|
|
352
351
|
render: (context) => renderGitPanel(html, controller, context),
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
// Generated from pi-web-plugins/git/server-plugin.ts. Do not edit directly.
|
|
2
2
|
import { basename, resolve } from "node:path";
|
|
3
3
|
import { requestGitBackend } from "./git-backend.js";
|
|
4
|
+
import { createWorkspaceDiscoveryHooks } from "./workspaceDiscoveryHooks.js";
|
|
4
5
|
const GIT_LOCAL_ENV_VARS = Object.freeze([
|
|
5
6
|
"GIT_ALTERNATE_OBJECT_DIRECTORIES",
|
|
6
7
|
"GIT_COMMON_DIR",
|
|
8
|
+
"GIT_CONFIG",
|
|
9
|
+
"GIT_CONFIG_COUNT",
|
|
10
|
+
"GIT_CONFIG_PARAMETERS",
|
|
7
11
|
"GIT_DIR",
|
|
12
|
+
"GIT_GRAFT_FILE",
|
|
13
|
+
"GIT_IMPLICIT_WORK_TREE",
|
|
8
14
|
"GIT_INDEX_FILE",
|
|
15
|
+
"GIT_NO_REPLACE_OBJECTS",
|
|
9
16
|
"GIT_OBJECT_DIRECTORY",
|
|
10
17
|
"GIT_PREFIX",
|
|
11
18
|
"GIT_QUARANTINE_PATH",
|
|
19
|
+
"GIT_REPLACE_REF_BASE",
|
|
20
|
+
"GIT_SHALLOW_FILE",
|
|
12
21
|
"GIT_WORK_TREE",
|
|
13
22
|
]);
|
|
14
23
|
const plugin = {
|
|
@@ -25,6 +34,7 @@ export default plugin;
|
|
|
25
34
|
export function createGitWorkspaceProvider(context) {
|
|
26
35
|
return Object.freeze({
|
|
27
36
|
fallback: true,
|
|
37
|
+
integration: createWorkspaceDiscoveryHooks(context, (cwd, args, signal) => runGit(context, cwd, args, signal)),
|
|
28
38
|
async probe(project, signal) {
|
|
29
39
|
const result = await runGit(context, project.path, ["rev-parse", "--is-inside-work-tree"], signal);
|
|
30
40
|
if (result.signal !== null)
|
|
@@ -161,6 +171,7 @@ async function runGit(context, cwd, args, signal) {
|
|
|
161
171
|
file: "git",
|
|
162
172
|
args: ["-C", cwd, ...args],
|
|
163
173
|
unsetEnv: GIT_LOCAL_ENV_VARS,
|
|
174
|
+
timeoutMs: 10_000,
|
|
164
175
|
signal,
|
|
165
176
|
});
|
|
166
177
|
if (result.stdoutTruncated || result.stderrTruncated) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Packaged with PI WEB: best-effort discovery hint, never Git authority.
|
|
2
|
+
const { spawnSync } = require("node:child_process");
|
|
3
|
+
const [notification, ...args] = process.argv.slice(2);
|
|
4
|
+
|
|
5
|
+
// Initial worktree checkout has a null previous OID and branch flag 1.
|
|
6
|
+
// --no-checkout skips this hook; ordinary catalog access still discovers it.
|
|
7
|
+
if (args.length === 3 && /^(?:0{40}|0{64})$/.test(args[0]) && args[2] === "1") {
|
|
8
|
+
try {
|
|
9
|
+
const command = JSON.parse(notification);
|
|
10
|
+
if (Array.isArray(command) && command.length > 0 && command.every((value) => typeof value === "string")) {
|
|
11
|
+
spawnSync(command[0], command.slice(1), { stdio: "ignore", timeout: 2000, killSignal: "SIGKILL" });
|
|
12
|
+
}
|
|
13
|
+
} catch { /* Invalid arguments, missing commands, and daemon failures must not affect Git. */ }
|
|
14
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// Generated from pi-web-plugins/git/workspaceDiscoveryHooks.ts. Do not edit directly.
|
|
2
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
3
|
+
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
5
|
+
const EVENT = "post-checkout";
|
|
6
|
+
/** Git owns the hook policy; the host owns settings snapshots and notification attribution/transport. */
|
|
7
|
+
export function createWorkspaceDiscoveryHooks(context, runGit, nodeExecutable = process.execPath) {
|
|
8
|
+
if (!isAbsolute(nodeExecutable))
|
|
9
|
+
throw new Error("Git hook runtime must be an absolute executable path");
|
|
10
|
+
// Serialize this activation's config edits, including registrations sharing a
|
|
11
|
+
// common Git config. Git itself locks writes from other processes.
|
|
12
|
+
let pending = Promise.resolve();
|
|
13
|
+
return Object.freeze({
|
|
14
|
+
reconcile(project, signal) {
|
|
15
|
+
const operation = pending.then(() => reconcile(project, signal));
|
|
16
|
+
pending = operation.catch(() => { });
|
|
17
|
+
return operation;
|
|
18
|
+
},
|
|
19
|
+
async acceptNotification(project, signal) {
|
|
20
|
+
return await enabled(project, signal) && (context.workspaceNotifications?.command(project.id).length ?? 0) > 0;
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
async function enabled(project, signal) {
|
|
24
|
+
signal.throwIfAborted();
|
|
25
|
+
const settings = context.resolveProjectSettings === undefined
|
|
26
|
+
? context.settings
|
|
27
|
+
: await context.resolveProjectSettings(project, signal);
|
|
28
|
+
const value = settings["workspaceDiscoveryHooks"];
|
|
29
|
+
if (value === undefined)
|
|
30
|
+
return true;
|
|
31
|
+
if (typeof value !== "boolean")
|
|
32
|
+
throw new Error("Git setting workspaceDiscoveryHooks must be a boolean");
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
async function reconcile(project, signal) {
|
|
36
|
+
const isEnabled = await enabled(project, signal);
|
|
37
|
+
// Separate projects/instances may share Git config. This identity belongs
|
|
38
|
+
// to the packaged-helper implementation; older generated helpers are untouched.
|
|
39
|
+
const identity = createHash("sha256")
|
|
40
|
+
.update(JSON.stringify(["packaged-helper", context.pluginId, resolve(context.dataDirectory), project.id]))
|
|
41
|
+
.digest("hex");
|
|
42
|
+
const name = `pi-web-workspace-discovery-${identity}`;
|
|
43
|
+
const helper = join(context.packageRoot, "workspace-discovery-notify.cjs");
|
|
44
|
+
const registration = join(context.dataDirectory, "workspace-discovery", `${name}.registration.json`);
|
|
45
|
+
const notifier = context.workspaceNotifications?.command(project.id);
|
|
46
|
+
const command = registrationCommand(helper, nodeExecutable, notifier ?? []);
|
|
47
|
+
const ownedCommands = new Set([command, ...await readRegistrationCommands(registration, signal)]);
|
|
48
|
+
const install = isEnabled && notifier !== undefined && notifier.length > 0;
|
|
49
|
+
if (install && !await supportsNamedHooks(project, signal))
|
|
50
|
+
return;
|
|
51
|
+
const commandKey = `hook.${name}.command`;
|
|
52
|
+
const eventKey = `hook.${name}.event`;
|
|
53
|
+
const commands = await readConfig(project, commandKey, signal);
|
|
54
|
+
const events = await readConfig(project, eventKey, signal);
|
|
55
|
+
if (commands.some((entry) => entry.scope !== "local" || !ownedCommands.has(entry.value))
|
|
56
|
+
|| events.some((entry) => entry.scope !== "local" || (entry.value !== EVENT && commands.length === 0))) {
|
|
57
|
+
// We only install local keys. A same-name global/worktree registration is
|
|
58
|
+
// external even if its values happen to match ours; never override it or
|
|
59
|
+
// remove the local command on which its inherited event might depend.
|
|
60
|
+
throw new Error(`Git workspace discovery hook conflict: ${name} has unexpected configuration`);
|
|
61
|
+
}
|
|
62
|
+
if (!install) {
|
|
63
|
+
// Disconnect the event first: an event without a command makes Git fail.
|
|
64
|
+
// Installation uses the reverse order for the same reason.
|
|
65
|
+
await unsetLocal(project, eventKey, events, signal);
|
|
66
|
+
await unsetLocal(project, commandKey, commands, signal);
|
|
67
|
+
signal.throwIfAborted();
|
|
68
|
+
await rm(registration, { force: true });
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
// Routing/runtime arguments live in Git config, not generated executable
|
|
72
|
+
// files. Preserve exact ownership before changing config, including either
|
|
73
|
+
// side of a failed update, then compact after installation succeeds.
|
|
74
|
+
await writeRegistration([...new Set([...commands.map((entry) => entry.value), command])]);
|
|
75
|
+
await setLocal(project, commandKey, command, commands, signal);
|
|
76
|
+
await setLocal(project, eventKey, EVENT, events, signal);
|
|
77
|
+
await writeRegistration([command]);
|
|
78
|
+
async function writeRegistration(owned) {
|
|
79
|
+
await writeOwnedFileIfChanged(registration, `${JSON.stringify({ version: 1, commands: owned })}\n`, signal);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function supportsNamedHooks(project, signal) {
|
|
83
|
+
// Detect Git's advertised named-config facility, not its version. `hook
|
|
84
|
+
// list` is unsuitable here: a missing command in an existing registration
|
|
85
|
+
// makes it fail, preventing precisely the repair we need to perform.
|
|
86
|
+
const result = await runGit(project.path, ["help", "--config"], signal);
|
|
87
|
+
if (result.signal !== null)
|
|
88
|
+
throw new Error(`Git named-hook detection ended from signal ${result.signal}`);
|
|
89
|
+
const keys = result.stdout.split(/\r?\n/u);
|
|
90
|
+
const supported = result.exitCode === 0
|
|
91
|
+
&& keys.includes("hook.<friendly-name>.command") && keys.includes("hook.<friendly-name>.event");
|
|
92
|
+
if (!supported)
|
|
93
|
+
context.logger.debug("Git named configuration hooks unavailable; workspace discovery remains on catalog access", { projectId: project.id });
|
|
94
|
+
return supported;
|
|
95
|
+
}
|
|
96
|
+
async function readConfig(project, key, signal) {
|
|
97
|
+
const result = await runGit(project.path, ["config", "--null", "--show-scope", "--get-all", key], signal);
|
|
98
|
+
if (result.signal === null && result.exitCode === 1)
|
|
99
|
+
return [];
|
|
100
|
+
requireSuccess(result, `read ${key}`);
|
|
101
|
+
const fields = result.stdout.split("\0");
|
|
102
|
+
if (fields.pop() !== "" || fields.length % 2 !== 0)
|
|
103
|
+
throw new Error(`Malformed Git config output for ${key}`);
|
|
104
|
+
const entries = [];
|
|
105
|
+
for (let index = 0; index < fields.length; index += 2) {
|
|
106
|
+
entries.push({ scope: fields[index] ?? "", value: fields[index + 1] ?? "" });
|
|
107
|
+
}
|
|
108
|
+
return entries;
|
|
109
|
+
}
|
|
110
|
+
async function setLocal(project, key, value, entries, signal) {
|
|
111
|
+
const local = entries.filter((entry) => entry.scope === "local");
|
|
112
|
+
if (local.length === 1 && local[0]?.value === value)
|
|
113
|
+
return;
|
|
114
|
+
requireSuccess(await runGit(project.path, ["config", "--local", "--replace-all", key, value], signal), `set ${key}`);
|
|
115
|
+
}
|
|
116
|
+
async function unsetLocal(project, key, entries, signal) {
|
|
117
|
+
if (!entries.some((entry) => entry.scope === "local"))
|
|
118
|
+
return;
|
|
119
|
+
requireSuccess(await runGit(project.path, ["config", "--local", "--unset-all", key], signal), `remove ${key}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function requireSuccess(result, action) {
|
|
123
|
+
if (result.signal === null && result.exitCode === 0)
|
|
124
|
+
return;
|
|
125
|
+
const outcome = result.signal === null ? `exit ${String(result.exitCode)}` : `signal ${result.signal}`;
|
|
126
|
+
throw new Error(`Unable to ${action} (${outcome}): ${result.stderr.trim()}`);
|
|
127
|
+
}
|
|
128
|
+
function shellQuote(value) {
|
|
129
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
130
|
+
}
|
|
131
|
+
function registrationCommand(helper, nodeExecutable, notificationCommand) {
|
|
132
|
+
// Git itself supplies the shell and appends quoted hook argv to the function
|
|
133
|
+
// call. No extra `sh` executable is required on the user's PATH. The final
|
|
134
|
+
// return preserves best-effort behavior even when our helper/runtime is gone.
|
|
135
|
+
const shellHelper = process.platform === "win32" ? helper.replaceAll("\\", "/") : helper;
|
|
136
|
+
const shellNode = process.platform === "win32" ? nodeExecutable.replaceAll("\\", "/") : nodeExecutable;
|
|
137
|
+
return `pi_web_workspace_notify () { if test -r ${shellQuote(shellHelper)}; then ${shellQuote(shellNode)} ${shellQuote(shellHelper)} ${shellQuote(JSON.stringify(notificationCommand))} "$@" >/dev/null 2>&1; fi; return 0; }; pi_web_workspace_notify`;
|
|
138
|
+
}
|
|
139
|
+
async function readRegistrationCommands(path, signal) {
|
|
140
|
+
let source;
|
|
141
|
+
try {
|
|
142
|
+
source = await readFile(path, { encoding: "utf8", signal });
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
146
|
+
return [];
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
const value = JSON.parse(source);
|
|
150
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)
|
|
151
|
+
|| Reflect.get(value, "version") !== 1)
|
|
152
|
+
throw new Error(`Invalid PI WEB Git hook registration record: ${path}`);
|
|
153
|
+
const commands = Reflect.get(value, "commands");
|
|
154
|
+
if (!Array.isArray(commands) || !commands.every((command) => typeof command === "string" && command.length > 0)) {
|
|
155
|
+
throw new Error(`Invalid PI WEB Git hook command record: ${path}`);
|
|
156
|
+
}
|
|
157
|
+
return commands;
|
|
158
|
+
}
|
|
159
|
+
async function writeOwnedFileIfChanged(path, source, signal) {
|
|
160
|
+
signal.throwIfAborted();
|
|
161
|
+
try {
|
|
162
|
+
if (await readFile(path, { encoding: "utf8", signal }) === source)
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT")
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
await mkdir(dirname(path), { recursive: true });
|
|
170
|
+
// Atomic per-project ownership records never contain executable code.
|
|
171
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
172
|
+
try {
|
|
173
|
+
await writeFile(temporary, source, { flag: "wx", mode: 0o600, signal });
|
|
174
|
+
signal.throwIfAborted();
|
|
175
|
+
await rename(temporary, path);
|
|
176
|
+
}
|
|
177
|
+
finally {
|
|
178
|
+
await rm(temporary, { force: true });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -198,7 +198,7 @@ function renderWorkspaceSection(html, workspace) {
|
|
|
198
198
|
</section>
|
|
199
199
|
`;
|
|
200
200
|
}
|
|
201
|
-
/** Panel body:
|
|
201
|
+
/** Panel body: machine status is useful even without a selected workspace. */
|
|
202
202
|
export function renderInfoPanel(html, context) {
|
|
203
203
|
return html `
|
|
204
204
|
<style>
|
|
@@ -214,9 +214,9 @@ export function renderInfoPanel(html, context) {
|
|
|
214
214
|
</style>
|
|
215
215
|
<section class="toolbar"><strong>Info</strong></section>
|
|
216
216
|
<section class="viewer info-status">
|
|
217
|
-
${renderStatusSection(html, context.state
|
|
217
|
+
${renderStatusSection(html, context.state.piWebStatus)}
|
|
218
218
|
${renderMachineSection(html, context.machine)}
|
|
219
|
-
${renderWorkspaceSection(html, context.workspace)}
|
|
219
|
+
${context.workspace === undefined ? null : renderWorkspaceSection(html, context.workspace)}
|
|
220
220
|
</section>
|
|
221
221
|
`;
|
|
222
222
|
}
|