@akira-tl/forgerelay 0.10.2 → 0.10.4
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/CHANGELOG.md +26 -0
- package/README.md +8 -7
- package/capabilities/shell-processes/GUIDE.md +3 -1
- package/dist/cli/setup-support.js +0 -15
- package/dist/cli/shell/setup.js +4 -1
- package/dist/cli.js +11 -3
- package/dist/mcp/process/process-platform.js +4 -0
- package/dist/mcp/process/process-sessions.js +1 -1
- package/dist/mcp/server/workspace/runtime/workspace-open-presentation.js +18 -3
- package/dist/mcp/server/workspace/runtime/workspace-open-schema.js +26 -0
- package/dist/mcp/server/workspace/runtime/workspace-open.js +7 -0
- package/dist/mcp/server-instructions.js +23 -1
- package/dist/runtime/shell/command-shell-runtime.js +3 -1
- package/dist/server.js +24 -6
- package/dist/ui/.vite/manifest.json +275 -275
- package/dist/ui/activity-panel-app.html +1 -1
- package/dist/ui/assets/{activity-panel-app-BkODhDLg.js → activity-panel-app-XlqSanXT.js} +1 -1
- package/dist/ui/assets/{angular-html-DjuELMdF.js → angular-html-tB9EchbM.js} +1 -1
- package/dist/ui/assets/{angular-ts-2EXVZIIX.js → angular-ts-BearSZDb.js} +1 -1
- package/dist/ui/assets/{apl-Bb4IeT5V.js → apl-CYQtr-y6.js} +1 -1
- package/dist/ui/assets/{astro-Bjavu3nT.js → astro-DDzqKIjK.js} +1 -1
- package/dist/ui/assets/{blade-DKW2uHZo.js → blade-DkwEyHvQ.js} +1 -1
- package/dist/ui/assets/{c-oLeHmzbj.js → c-Dv-N5hjn.js} +1 -1
- package/dist/ui/assets/{cobol-CcqFMr5T.js → cobol-xBn5JxHE.js} +1 -1
- package/dist/ui/assets/{coffee-BA20zqRF.js → coffee-BaXXvoJw.js} +1 -1
- package/dist/ui/assets/{cpp-D750WmDa.js → cpp-C3unXX-3.js} +1 -1
- package/dist/ui/assets/{crystal-Cv5I6OMI.js → crystal-DjOpXVU1.js} +1 -1
- package/dist/ui/assets/{css-CjPPWPeH.js → css-8Xr4kNNH.js} +1 -1
- package/dist/ui/assets/{edge-aYRuudEZ.js → edge-DNaRizoR.js} +1 -1
- package/dist/ui/assets/{elixir-ChLitmz7.js → elixir-CwBMwZmy.js} +1 -1
- package/dist/ui/assets/{elm-Fs7L7u36.js → elm-gOUD2CW3.js} +1 -1
- package/dist/ui/assets/{erb-B-qXHxN1.js → erb-BcR-fjUp.js} +1 -1
- package/dist/ui/assets/{git-rebase-DOVN5OEg.js → git-rebase-BAQPKHF6.js} +1 -1
- package/dist/ui/assets/{glimmer-js-BFt_vCTW.js → glimmer-js-P_wOfSz_.js} +1 -1
- package/dist/ui/assets/{glimmer-ts-ufCHlR6C.js → glimmer-ts-B44aY1fZ.js} +1 -1
- package/dist/ui/assets/{glsl-Ex4JXGuC.js → glsl-BvCQg9MI.js} +1 -1
- package/dist/ui/assets/{graphql-pQPXtBk5.js → graphql-C9S90gn_.js} +1 -1
- package/dist/ui/assets/{hack-B5hDguhd.js → hack-ddU2k0nt.js} +1 -1
- package/dist/ui/assets/{haml-BrNeI29U.js → haml-Bb1w9rMG.js} +1 -1
- package/dist/ui/assets/{handlebars-DCqTsL9-.js → handlebars-BpzaUpmz.js} +1 -1
- package/dist/ui/assets/{heavy-payload-DsmmHgvt.js → heavy-payload-CJKqi8Ac.js} +3 -3
- package/dist/ui/assets/{html-D8k0CC9V.js → html-dUOs5pA9.js} +1 -1
- package/dist/ui/assets/{html-derivative-CC8flWmC.js → html-derivative-B2beVdmA.js} +1 -1
- package/dist/ui/assets/{http-BYnWqNep.js → http-iEskZy8U.js} +1 -1
- package/dist/ui/assets/{hurl-BND-z85s.js → hurl-BuUsLSKv.js} +1 -1
- package/dist/ui/assets/{java-Dug0oB9T.js → java-BFZCrORY.js} +1 -1
- package/dist/ui/assets/{javascript-oDJ7WITC.js → javascript-DaIQ0Zqj.js} +1 -1
- package/dist/ui/assets/{jinja-Cv0t_F1U.js → jinja-CRpkWwrH.js} +1 -1
- package/dist/ui/assets/{jison-Car3MF55.js → jison-BYfSnuHj.js} +1 -1
- package/dist/ui/assets/{json-CEViCsf4.js → json-BQC_Yp48.js} +1 -1
- package/dist/ui/assets/{jsx-BfZ0zgSH.js → jsx-Dd-CJRf9.js} +1 -1
- package/dist/ui/assets/{julia-CmX0b3Tn.js → julia-B8Rxy7vD.js} +1 -1
- package/dist/ui/assets/{just-BuugLCVk.js → just-DzhIWpFP.js} +1 -1
- package/dist/ui/assets/{latex-FpTXqO03.js → latex-CUNjUUL-.js} +1 -1
- package/dist/ui/assets/{liquid-B37OzUFu.js → liquid-hUg7nC61.js} +1 -1
- package/dist/ui/assets/{lua-BHjcb8oq.js → lua-BoZTiL2I.js} +1 -1
- package/dist/ui/assets/{marko-CApj_fsH.js → marko-CQmIkwMF.js} +1 -1
- package/dist/ui/assets/{mdc-Ed5vQWq8.js → mdc-CB-6dHYa.js} +1 -1
- package/dist/ui/assets/{nginx-u_rVEBox.js → nginx-CQORo_Bt.js} +1 -1
- package/dist/ui/assets/{nim-Ba9EdchT.js → nim-BNwBNzdn.js} +1 -1
- package/dist/ui/assets/{perl-BpU_6Wvz.js → perl-Cnz14fAJ.js} +1 -1
- package/dist/ui/assets/{php-bKabofky.js → php-YywDo63b.js} +1 -1
- package/dist/ui/assets/{pug-C_aLNkqe.js → pug-BwbJp4cM.js} +1 -1
- package/dist/ui/assets/{qml-D9VisnRy.js → qml-D2DY6dMH.js} +1 -1
- package/dist/ui/assets/{r-CYqB1sLx.js → r-B5nYsy-M.js} +1 -1
- package/dist/ui/assets/{razor-DNJKI8Rs.js → razor-pMTG7k41.js} +1 -1
- package/dist/ui/assets/{regexp-DHrTHXkz.js → regexp-oP0lbks_.js} +1 -1
- package/dist/ui/assets/{rst-CbNdSFHv.js → rst-CO_ndDm4.js} +1 -1
- package/dist/ui/assets/{ruby-FyKG-pkF.js → ruby-xzRn9gTh.js} +1 -1
- package/dist/ui/assets/{sas-BbSPDl3X.js → sas-CBdtFKLA.js} +1 -1
- package/dist/ui/assets/{scss-2nX9g6gD.js → scss-CEqgKQNf.js} +1 -1
- package/dist/ui/assets/{shellscript-CmON6x2x.js → shellscript-DzJJiWQC.js} +1 -1
- package/dist/ui/assets/{shellsession-DtqIGlAX.js → shellsession-BaZnm5rZ.js} +1 -1
- package/dist/ui/assets/{soy-BzUJMmJT.js → soy-IouG3xdB.js} +1 -1
- package/dist/ui/assets/{sql-CgVcFZmb.js → sql-DC2rdTzV.js} +1 -1
- package/dist/ui/assets/{stata-CNitlMot.js → stata-dgbkIZHP.js} +1 -1
- package/dist/ui/assets/{surrealql-BA8-xsRZ.js → surrealql-CBROnw_p.js} +1 -1
- package/dist/ui/assets/{svelte-El02yAxv.js → svelte-6r6BpX4-.js} +1 -1
- package/dist/ui/assets/{templ-DeUQ84aR.js → templ-BKD_PsbL.js} +1 -1
- package/dist/ui/assets/{tex-Cu-FqpPS.js → tex-CKY1bwHb.js} +1 -1
- package/dist/ui/assets/{ts-tags-X42N6TGA.js → ts-tags-BiYP5N_C.js} +1 -1
- package/dist/ui/assets/{tsx-C1aPp9rY.js → tsx-D2OMiPNF.js} +1 -1
- package/dist/ui/assets/{twig-Cj1Q4SQc.js → twig-BTe1oPFW.js} +1 -1
- package/dist/ui/assets/{typescript-Du9lVt1G.js → typescript-BGIABksB.js} +1 -1
- package/dist/ui/assets/{vue-_uMGbFv8.js → vue-CT0IiySh.js} +1 -1
- package/dist/ui/assets/{vue-html-D61naG-Q.js → vue-html-B-tXcbat.js} +1 -1
- package/dist/ui/assets/{vue-vine-CkFae4yV.js → vue-vine-C5ZyDCrs.js} +1 -1
- package/dist/ui/assets/{xml-Cbwsc6ws.js → xml-CfeBNlMP.js} +1 -1
- package/dist/ui/assets/{xsl-NyBx72XB.js → xsl-DvDSndNI.js} +1 -1
- package/dist/ui/assets/{yaml-Cgb6Uq9u.js → yaml-umbj2r1g.js} +1 -1
- package/dist/workspaces/presentation/workspace-presentation.js +22 -0
- package/dist/workspaces/relay/tests/test-support.js +24 -2
- package/dist/workspaces/relay/workspace-relay.js +6 -0
- package/docs/gotchas.md +1 -4
- package/docs/roadmap.md +3 -2
- package/package.json +4 -2
- package/scripts/ci/cmd-acceptance.mjs +295 -0
- package/scripts/ci/windows-product-acceptance.mjs +321 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import assert from "node:assert/strict";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { pathToFileURL } from "node:url";
|
|
9
|
+
import { spawnSync } from "node:child_process";
|
|
10
|
+
|
|
11
|
+
if (process.platform !== "win32") {
|
|
12
|
+
console.log("Windows packaged product acceptance skipped outside Windows.");
|
|
13
|
+
process.exit(0);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const npmCli = process.env.npm_execpath;
|
|
17
|
+
if (!npmCli) throw new Error("Windows product acceptance must run through npm so npm_execpath is available");
|
|
18
|
+
|
|
19
|
+
const root = await import("node:fs/promises").then(({ mkdtemp }) =>
|
|
20
|
+
mkdtemp(join(tmpdir(), "forgerelay-windows-product-acceptance-"))
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const artifactDir = join(root, "artifact");
|
|
25
|
+
const prefix = join(root, "prefix");
|
|
26
|
+
const projectRoot = join(root, "project");
|
|
27
|
+
await Promise.all([
|
|
28
|
+
mkdir(artifactDir, { recursive: true }),
|
|
29
|
+
mkdir(prefix, { recursive: true }),
|
|
30
|
+
mkdir(projectRoot, { recursive: true }),
|
|
31
|
+
]);
|
|
32
|
+
await writeFile(join(projectRoot, "README.md"), "Windows product acceptance workspace\n", "utf8");
|
|
33
|
+
|
|
34
|
+
const packed = runNpm(["pack", "--json", "--pack-destination", artifactDir]);
|
|
35
|
+
const packResult = JSON.parse(packed.stdout);
|
|
36
|
+
const filename = packResult?.[0]?.filename;
|
|
37
|
+
if (!filename) throw new Error(`npm pack did not report a package filename: ${packed.stdout}`);
|
|
38
|
+
const tarball = join(artifactDir, filename);
|
|
39
|
+
assert.ok(existsSync(tarball), `packed artifact is missing: ${tarball}`);
|
|
40
|
+
|
|
41
|
+
runNpm(["install", "--global", "--prefix", prefix, tarball]);
|
|
42
|
+
const shim = join(prefix, "forgerelay.cmd");
|
|
43
|
+
assert.ok(existsSync(shim), `npm did not create the Windows launcher shim: ${shim}`);
|
|
44
|
+
|
|
45
|
+
const installedRoot = join(prefix, "node_modules", "@akira-tl", "forgerelay");
|
|
46
|
+
assert.ok(existsSync(join(installedRoot, "dist", "cli.js")), `installed package is missing dist/cli.js: ${installedRoot}`);
|
|
47
|
+
const workspaceProbe = join(root, "packaged-workspace-probe.mjs");
|
|
48
|
+
await writeFile(workspaceProbe, packagedWorkspaceProbe(), "utf8");
|
|
49
|
+
|
|
50
|
+
const pwsh = resolveWhere("pwsh.exe", "PowerShell 7");
|
|
51
|
+
const powershell = resolveWhere("powershell.exe", "Windows PowerShell 5.1");
|
|
52
|
+
const cmd = process.env.ComSpec ?? process.env.COMSPEC;
|
|
53
|
+
assert.ok(cmd && existsSync(cmd), `Windows product acceptance requires a valid ComSpec; got ${cmd ?? "unset"}.`);
|
|
54
|
+
|
|
55
|
+
const runtimes = [
|
|
56
|
+
{ family: "pwsh", executable: pwsh, doctorIdentity: /Command shell: pwsh / },
|
|
57
|
+
{ family: "powershell", executable: powershell, doctorIdentity: /Command shell: powershell 5\.1\./ },
|
|
58
|
+
{ family: "cmd", executable: cmd, doctorIdentity: /Command shell: cmd / },
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
for (const runtime of runtimes) {
|
|
62
|
+
await exercisePackagedRuntime({
|
|
63
|
+
...runtime,
|
|
64
|
+
root,
|
|
65
|
+
projectRoot,
|
|
66
|
+
shim,
|
|
67
|
+
installedRoot,
|
|
68
|
+
workspaceProbe,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
await exercisePackagedElevationContract(installedRoot, { root, projectRoot, shim, cmd });
|
|
73
|
+
console.log("Windows packaged product acceptance passed for pwsh, Windows PowerShell 5.1, cmd.exe, editable Instructions, and elevated startup protection.");
|
|
74
|
+
} finally {
|
|
75
|
+
await rm(root, { recursive: true, force: true });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function exercisePackagedRuntime({ family, executable, doctorIdentity, root, projectRoot, shim, installedRoot, workspaceProbe }) {
|
|
79
|
+
const configDir = join(root, `config-${family}`);
|
|
80
|
+
const stateDir = join(root, `state-${family}`);
|
|
81
|
+
const instructionsDir = join(configDir, "instructions");
|
|
82
|
+
const instructionPath = join(instructionsDir, `${family}.md`);
|
|
83
|
+
const instructionMarker = `WINDOWS-PACKAGED-${family.toUpperCase()}-EDITABLE-INSTRUCTION`;
|
|
84
|
+
await Promise.all([
|
|
85
|
+
mkdir(configDir, { recursive: true }),
|
|
86
|
+
mkdir(stateDir, { recursive: true }),
|
|
87
|
+
mkdir(instructionsDir, { recursive: true }),
|
|
88
|
+
]);
|
|
89
|
+
await writeFile(
|
|
90
|
+
instructionPath,
|
|
91
|
+
`# User-edited ${family} Instructions\n\n${instructionMarker}\n`,
|
|
92
|
+
"utf8",
|
|
93
|
+
);
|
|
94
|
+
await writeFile(
|
|
95
|
+
join(configDir, "config.json"),
|
|
96
|
+
JSON.stringify({
|
|
97
|
+
host: "127.0.0.1",
|
|
98
|
+
port: 7678,
|
|
99
|
+
allowedRoots: [projectRoot],
|
|
100
|
+
stateDir,
|
|
101
|
+
worktreeRoot: join(root, `worktrees-${family}`),
|
|
102
|
+
commandShell: {
|
|
103
|
+
mode: "pinned",
|
|
104
|
+
family,
|
|
105
|
+
executable,
|
|
106
|
+
},
|
|
107
|
+
shellInstructions: true,
|
|
108
|
+
skillsEnabled: false,
|
|
109
|
+
}, null, 2),
|
|
110
|
+
"utf8",
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const env = cleanAcceptanceEnv(configDir);
|
|
114
|
+
const doctor = runPackagedShim(shim, ["doctor"], env);
|
|
115
|
+
assert.match(doctor.stdout, doctorIdentity);
|
|
116
|
+
assert.match(doctor.stdout, /Command shell source: explicit/);
|
|
117
|
+
assert.match(doctor.stdout, /Command shell compatibility: /);
|
|
118
|
+
assert.match(doctor.stdout, /Shell Instructions: enabled \(.+; available\)/);
|
|
119
|
+
assert.doesNotMatch(doctor.stdout, /Bash shell:/);
|
|
120
|
+
|
|
121
|
+
const probeResultPath = join(root, `workspace-probe-${family}.json`);
|
|
122
|
+
const probe = spawnSync(
|
|
123
|
+
process.execPath,
|
|
124
|
+
[
|
|
125
|
+
workspaceProbe,
|
|
126
|
+
installedRoot,
|
|
127
|
+
projectRoot,
|
|
128
|
+
stateDir,
|
|
129
|
+
instructionPath,
|
|
130
|
+
instructionMarker,
|
|
131
|
+
`windows-product-${family}`,
|
|
132
|
+
probeResultPath,
|
|
133
|
+
],
|
|
134
|
+
{
|
|
135
|
+
cwd: process.cwd(),
|
|
136
|
+
env,
|
|
137
|
+
encoding: "utf8",
|
|
138
|
+
windowsHide: true,
|
|
139
|
+
timeout: 20_000,
|
|
140
|
+
},
|
|
141
|
+
);
|
|
142
|
+
if (probe.error || probe.status !== 0) {
|
|
143
|
+
throw new Error(`Packaged Workspace probe failed for ${family}: ${probe.error?.message ?? probe.stderr ?? probe.status}`);
|
|
144
|
+
}
|
|
145
|
+
const opened = JSON.parse(await readFile(probeResultPath, "utf8"));
|
|
146
|
+
assert.equal(opened.workspaceRoot, projectRoot);
|
|
147
|
+
assert.equal(opened.instructionPath, instructionPath);
|
|
148
|
+
assert.match(opened.instructionContent ?? "", new RegExp(instructionMarker));
|
|
149
|
+
assert.equal(opened.instructionStatus, "loaded", `${family} shell Instructions were not advertised as loaded`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function exercisePackagedElevationContract(installedRoot, { root, projectRoot, shim, cmd }) {
|
|
153
|
+
const { assertRuntimePrivilegeAllowed, detectRuntimePrivilege, elevatedRuntimeWarning } = await importInstalled(
|
|
154
|
+
installedRoot,
|
|
155
|
+
"dist/runtime/security/runtime-privilege.js",
|
|
156
|
+
);
|
|
157
|
+
const actualPrivilege = detectRuntimePrivilege();
|
|
158
|
+
if (actualPrivilege.level !== "standard") {
|
|
159
|
+
const configDir = join(root, "config-elevated-refusal");
|
|
160
|
+
const stateDir = join(root, "state-elevated-refusal");
|
|
161
|
+
await Promise.all([
|
|
162
|
+
mkdir(configDir, { recursive: true }),
|
|
163
|
+
mkdir(stateDir, { recursive: true }),
|
|
164
|
+
]);
|
|
165
|
+
await writeFile(
|
|
166
|
+
join(configDir, "config.json"),
|
|
167
|
+
JSON.stringify({
|
|
168
|
+
host: "127.0.0.1",
|
|
169
|
+
port: 7678,
|
|
170
|
+
allowedRoots: [projectRoot],
|
|
171
|
+
stateDir,
|
|
172
|
+
worktreeRoot: join(root, "worktrees-elevated-refusal"),
|
|
173
|
+
commandShell: { mode: "pinned", family: "cmd", executable: cmd },
|
|
174
|
+
shellInstructions: false,
|
|
175
|
+
}, null, 2),
|
|
176
|
+
"utf8",
|
|
177
|
+
);
|
|
178
|
+
const refused = runPackagedShimResult(shim, ["serve"], cleanAcceptanceEnv(configDir));
|
|
179
|
+
assert.equal(refused.status, 1, `elevated packaged serve should fail closed, got ${refused.status}`);
|
|
180
|
+
assert.match(
|
|
181
|
+
`${refused.stdout ?? ""}\n${refused.stderr ?? ""}`,
|
|
182
|
+
/refuses to start its Agent\/Hook runtime with system-level or unknown privilege by default/,
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const elevated = {
|
|
187
|
+
level: "elevated",
|
|
188
|
+
platform: "win32",
|
|
189
|
+
source: "windows-token",
|
|
190
|
+
detail: "packaged acceptance synthetic high-integrity token",
|
|
191
|
+
};
|
|
192
|
+
assert.throws(
|
|
193
|
+
() => assertRuntimePrivilegeAllowed(elevated, false),
|
|
194
|
+
/refuses to start its Agent\/Hook runtime with system-level or unknown privilege by default/,
|
|
195
|
+
);
|
|
196
|
+
assert.doesNotThrow(() => assertRuntimePrivilegeAllowed(elevated, true));
|
|
197
|
+
assert.match(elevatedRuntimeWarning(elevated), /system-wide or irreversible changes/);
|
|
198
|
+
|
|
199
|
+
const unknown = {
|
|
200
|
+
level: "unknown",
|
|
201
|
+
platform: "win32",
|
|
202
|
+
source: "windows-token",
|
|
203
|
+
detail: "packaged acceptance token inspection unavailable",
|
|
204
|
+
};
|
|
205
|
+
assert.throws(
|
|
206
|
+
() => assertRuntimePrivilegeAllowed(unknown, false),
|
|
207
|
+
/could not safely determine whether this process is elevated/,
|
|
208
|
+
);
|
|
209
|
+
assert.doesNotThrow(() => assertRuntimePrivilegeAllowed(unknown, true));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function packagedWorkspaceProbe() {
|
|
213
|
+
return String.raw`
|
|
214
|
+
import { writeFile } from "node:fs/promises";
|
|
215
|
+
import { join } from "node:path";
|
|
216
|
+
import { pathToFileURL } from "node:url";
|
|
217
|
+
|
|
218
|
+
const [installedRoot, projectRoot, stateDir, instructionPath, instructionMarker, conversationScopeId, resultPath] = process.argv.slice(2);
|
|
219
|
+
const importInstalled = (relativePath) => import(pathToFileURL(join(installedRoot, relativePath)).href);
|
|
220
|
+
const [{ loadConfig }, { SqliteWorkspaceStore }, { WorkspaceRegistry }] = await Promise.all([
|
|
221
|
+
importInstalled("dist/runtime/config/config.js"),
|
|
222
|
+
importInstalled("dist/workspaces/state/workspace-store.js"),
|
|
223
|
+
importInstalled("dist/workspaces.js"),
|
|
224
|
+
]);
|
|
225
|
+
const config = loadConfig(process.env);
|
|
226
|
+
const store = new SqliteWorkspaceStore(stateDir);
|
|
227
|
+
try {
|
|
228
|
+
const registry = new WorkspaceRegistry(config, store);
|
|
229
|
+
const opened = await registry.openWorkspace(
|
|
230
|
+
{ path: projectRoot, context: "full" },
|
|
231
|
+
{ conversationScopeId },
|
|
232
|
+
);
|
|
233
|
+
const loadedShellInstruction = opened.agentsFiles.find((file) => file.path === instructionPath);
|
|
234
|
+
const instructionState = opened.workspace.workspaceInstructions.find((entry) => entry.path === instructionPath);
|
|
235
|
+
if (!loadedShellInstruction || !loadedShellInstruction.content.includes(instructionMarker)) {
|
|
236
|
+
throw new Error("Packaged shell Instructions were not loaded from the editable instruction file.");
|
|
237
|
+
}
|
|
238
|
+
await writeFile(resultPath, JSON.stringify({
|
|
239
|
+
workspaceRoot: opened.workspace.root,
|
|
240
|
+
instructionPath: loadedShellInstruction.path,
|
|
241
|
+
instructionContent: loadedShellInstruction.content,
|
|
242
|
+
instructionStatus: instructionState?.status ?? null,
|
|
243
|
+
}), "utf8");
|
|
244
|
+
} finally {
|
|
245
|
+
store.close();
|
|
246
|
+
}
|
|
247
|
+
`;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function cleanAcceptanceEnv(configDir) {
|
|
251
|
+
const env = Object.fromEntries(
|
|
252
|
+
Object.entries(process.env).filter(([name]) => !name.startsWith("FORGERELAY_")),
|
|
253
|
+
);
|
|
254
|
+
delete env.npm_lifecycle_event;
|
|
255
|
+
return {
|
|
256
|
+
...env,
|
|
257
|
+
FORGERELAY_CONFIG_DIR: configDir,
|
|
258
|
+
FORGERELAY_OAUTH_OWNER_TOKEN: "windows-product-acceptance-owner-token-long-enough",
|
|
259
|
+
FORGERELAY_SKILLS: "0",
|
|
260
|
+
FORGERELAY_WIDGETS: "off",
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function runPackagedShim(shim, args, env) {
|
|
265
|
+
const result = runPackagedShimResult(shim, args, env);
|
|
266
|
+
if (result.error || result.status !== 0) {
|
|
267
|
+
throw new Error(`Packaged launcher failed (${args.join(" ")}): ${result.error?.message ?? result.stderr ?? result.status}`);
|
|
268
|
+
}
|
|
269
|
+
return result;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function runPackagedShimResult(shim, args, env) {
|
|
273
|
+
const cmd = process.env.ComSpec ?? process.env.COMSPEC ?? "cmd.exe";
|
|
274
|
+
const command = [`\"${shim}\"`, ...args.map(quoteCmdArg)].join(" ");
|
|
275
|
+
return spawnSync(
|
|
276
|
+
cmd,
|
|
277
|
+
["/d", "/s", "/c", `"${command}"`],
|
|
278
|
+
{
|
|
279
|
+
cwd: process.cwd(),
|
|
280
|
+
env,
|
|
281
|
+
encoding: "utf8",
|
|
282
|
+
windowsHide: true,
|
|
283
|
+
windowsVerbatimArguments: true,
|
|
284
|
+
timeout: 10_000,
|
|
285
|
+
},
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function resolveWhere(executable, label) {
|
|
290
|
+
const result = spawnSync("where.exe", [executable], {
|
|
291
|
+
encoding: "utf8",
|
|
292
|
+
windowsHide: true,
|
|
293
|
+
});
|
|
294
|
+
if (result.error || result.status !== 0) {
|
|
295
|
+
throw new Error(`Windows product acceptance requires ${label} (${executable}) on PATH.`);
|
|
296
|
+
}
|
|
297
|
+
const resolved = result.stdout?.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
|
|
298
|
+
if (!resolved) throw new Error(`where.exe reported no ${executable} path.`);
|
|
299
|
+
return resolved;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function runNpm(args) {
|
|
303
|
+
const result = spawnSync(process.execPath, [npmCli, ...args], {
|
|
304
|
+
cwd: process.cwd(),
|
|
305
|
+
env: process.env,
|
|
306
|
+
encoding: "utf8",
|
|
307
|
+
windowsHide: true,
|
|
308
|
+
});
|
|
309
|
+
if (result.error || result.status !== 0) {
|
|
310
|
+
throw new Error(`npm ${args.join(" ")} failed: ${result.error?.message ?? result.stderr ?? result.status}`);
|
|
311
|
+
}
|
|
312
|
+
return result;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function importInstalled(installedRoot, relativePath) {
|
|
316
|
+
return import(pathToFileURL(join(installedRoot, relativePath)).href);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function quoteCmdArg(value) {
|
|
320
|
+
return `"${String(value).replaceAll('"', '""')}"`;
|
|
321
|
+
}
|