@magnitudedev/pi-extension 0.0.1-alpha.1 → 0.0.1-alpha.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 +26 -7
- package/dist/magnitude-plugin.json +4 -4
- package/dist/magnitude.js +377 -86
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -10,9 +10,15 @@ pi install npm:@magnitudedev/pi-extension
|
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
On the next fresh interactive Pi launch, Magnitude asks **“Set up local models with Magnitude now?”**
|
|
13
|
-
Yes
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
Yes opens Magnitude's actual graphical setup in the same terminal, including its model rankings,
|
|
14
|
+
radar, downloads, and loading progress. It connects Pi automatically at the final step, then returns
|
|
15
|
+
to the same Pi conversation with the selected model active. No agent prompt or cloud credentials
|
|
16
|
+
are needed. If Magnitude is missing, accepting setup installs the CLI with
|
|
17
|
+
`npm install --global @magnitudedev/cli` before opening onboarding. Pi stays visible with its native
|
|
18
|
+
spinner: “Installing Magnitude…”. Installer output is suppressed; failures
|
|
19
|
+
show a concise diagnostic. Escape cancels preparation, and `/magnitude-setup` retries.
|
|
20
|
+
An existing CLI is preserved and must support hosted setup; an incompatible version reports an
|
|
21
|
+
update requirement rather than being silently replaced.
|
|
16
22
|
|
|
17
23
|
No or Escape leaves “You can set up local models anytime with `/magnitude-setup`.” in the conversation.
|
|
18
24
|
The offer is remembered per Pi profile across restarts, reloads, and package updates. Existing
|
|
@@ -20,9 +26,9 @@ Magnitude model configurations, non-interactive modes, conversations, and startu
|
|
|
20
26
|
alone. Run `/magnitude-setup` whenever you want to start onboarding yourself.
|
|
21
27
|
|
|
22
28
|
The package includes the Magnitude usage skill; an already-loaded skill of that name takes
|
|
23
|
-
precedence without a collision warning. `--no-skills` disables this fallback too.
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
precedence without a collision warning. `--no-skills` disables this fallback too. No separate CLI
|
|
30
|
+
installation is needed. npm must be available with a writable global prefix, as for a normal npm
|
|
31
|
+
CLI installation. Merely loading the extension does not install software.
|
|
26
32
|
|
|
27
33
|
If you already have Magnitude and a local model, connect directly:
|
|
28
34
|
|
|
@@ -49,7 +55,7 @@ latency, and token-weighted generation throughput. Pi extensions execute with yo
|
|
|
49
55
|
|
|
50
56
|
Commands:
|
|
51
57
|
|
|
52
|
-
- `/magnitude-setup` —
|
|
58
|
+
- `/magnitude-setup` — open Magnitude's graphical model setup inside Pi
|
|
53
59
|
- `/load-model [model-id]` — load an installed model
|
|
54
60
|
- `/stop-model` — stop the active model
|
|
55
61
|
|
|
@@ -74,3 +80,16 @@ builds and runs the checkout's inference runtime,
|
|
|
74
80
|
and keeps the current source CLI available until Pi exits. Exiting Pi stops the development runtime
|
|
75
81
|
and restores the service state that existed before launch. The launcher inherits the current
|
|
76
82
|
environment; it does not start or configure tracing.
|
|
83
|
+
|
|
84
|
+
To test the first-run setup offer with no preconfigured Magnitude provider:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
bun run dev:pi --setup
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
This installs only the checkout's package into the temporary Pi profile. Accept the setup offer,
|
|
91
|
+
choose a model, and return to Pi to chat. Both development modes keep connection files and skills
|
|
92
|
+
inside the temporary profile, and do not change login-startup registration. They share the machine's
|
|
93
|
+
real model storage and inference service: downloads consume disk space and selected models use
|
|
94
|
+
memory. The launcher restores the prior managed service after Pi exits. Ordinary installed setup
|
|
95
|
+
does enable login startup.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magnitudedev/pi-extension",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.2",
|
|
4
4
|
"rpcVersion": 1,
|
|
5
|
-
"contentFingerprint": "
|
|
5
|
+
"contentFingerprint": "5661646da7080511725cdef888b7ce04148c99b56fbe6454619754ebc5235718",
|
|
6
6
|
"files": {
|
|
7
|
-
"README.md": "
|
|
8
|
-
"dist/magnitude.js": "
|
|
7
|
+
"README.md": "0334364928717efd1b146486d6dc2d9ff0f28b77ff28b162d04351738170b581",
|
|
8
|
+
"dist/magnitude.js": "915f1d6bb080180a774b1bd009b3d7bc6c5cbab94fd7ab067dd3345d638538ad",
|
|
9
9
|
"dist/skills/magnitude/SKILL.md": "37de67f0c7b831415f6298c8785b7ae97d5cfe454dc778651d96114840d2ace2"
|
|
10
10
|
}
|
|
11
11
|
}
|
package/dist/magnitude.js
CHANGED
|
@@ -5842,19 +5842,318 @@ var registerMagnitudeCommands = (pi, sdk = clientLayer2()) => {
|
|
|
5842
5842
|
};
|
|
5843
5843
|
|
|
5844
5844
|
// extensions/onboarding.ts
|
|
5845
|
-
import * as
|
|
5845
|
+
import * as FileSystem3 from "@effect/platform/FileSystem";
|
|
5846
5846
|
import * as NodeFileSystem2 from "@effect/platform-node/NodeFileSystem";
|
|
5847
5847
|
import { getAgentDir, parseArgs } from "@earendil-works/pi-coding-agent";
|
|
5848
|
-
import { Effect as
|
|
5848
|
+
import { Effect as Effect13, Exit as Exit4, Scope as Scope5 } from "effect";
|
|
5849
5849
|
import { fileURLToPath } from "node:url";
|
|
5850
|
-
|
|
5850
|
+
|
|
5851
|
+
// extensions/setup.ts
|
|
5852
|
+
import * as Command2 from "@effect/platform/Command";
|
|
5853
|
+
import * as CommandExecutor3 from "@effect/platform/CommandExecutor";
|
|
5854
|
+
import * as FileSystem from "@effect/platform/FileSystem";
|
|
5855
|
+
import * as NodeContext from "@effect/platform-node/NodeContext";
|
|
5856
|
+
import { delimiter, join } from "node:path";
|
|
5857
|
+
import { stripVTControlCharacters } from "node:util";
|
|
5858
|
+
import { Context as Context7, Effect as Effect12, Exit as Exit3, Fiber as Fiber2, Layer as Layer6, ManagedRuntime as ManagedRuntime2, Schema as Schema53, Scope as Scope4, Stream as Stream7 } from "effect";
|
|
5859
|
+
import { CancellableLoader } from "@earendil-works/pi-tui";
|
|
5860
|
+
|
|
5861
|
+
// ../../packages/utils/src/process/interactive-process.ts
|
|
5862
|
+
import { spawn } from "node:child_process";
|
|
5863
|
+
import { Deferred as Deferred3, Duration as Duration2, Effect as Effect11, Option as Option17, Schema as Schema51 } from "effect";
|
|
5864
|
+
|
|
5865
|
+
class InteractiveProcessFailed extends Schema51.TaggedError()("InteractiveProcessFailed", {
|
|
5866
|
+
operation: Schema51.Literal("spawn", "run"),
|
|
5867
|
+
message: Schema51.String
|
|
5868
|
+
}) {
|
|
5869
|
+
}
|
|
5870
|
+
var messageFrom = (cause) => cause instanceof Error ? cause.message : String(cause);
|
|
5871
|
+
var definedEnvironment = (environment) => Object.fromEntries(Object.entries(environment).filter((entry) => entry[1] !== undefined));
|
|
5872
|
+
var acquireInteractiveProcess = (input) => Effect11.gen(function* () {
|
|
5873
|
+
const termination = yield* Deferred3.make();
|
|
5874
|
+
const child = yield* Effect11.async((resume) => {
|
|
5875
|
+
let acquired = false;
|
|
5876
|
+
let handle;
|
|
5877
|
+
try {
|
|
5878
|
+
handle = spawn(input.executable, [...input.args], {
|
|
5879
|
+
cwd: input.workingDirectory,
|
|
5880
|
+
detached: false,
|
|
5881
|
+
env: definedEnvironment(input.environment),
|
|
5882
|
+
shell: false,
|
|
5883
|
+
stdio: "inherit"
|
|
5884
|
+
});
|
|
5885
|
+
} catch (cause) {
|
|
5886
|
+
resume(Effect11.fail(new InteractiveProcessFailed({
|
|
5887
|
+
operation: "spawn",
|
|
5888
|
+
message: messageFrom(cause)
|
|
5889
|
+
})));
|
|
5890
|
+
return;
|
|
5891
|
+
}
|
|
5892
|
+
const onError = (cause) => {
|
|
5893
|
+
const failure = new InteractiveProcessFailed({
|
|
5894
|
+
operation: acquired ? "run" : "spawn",
|
|
5895
|
+
message: messageFrom(cause)
|
|
5896
|
+
});
|
|
5897
|
+
if (acquired) {
|
|
5898
|
+
Deferred3.unsafeDone(termination, Effect11.fail(failure));
|
|
5899
|
+
} else {
|
|
5900
|
+
resume(Effect11.fail(failure));
|
|
5901
|
+
}
|
|
5902
|
+
};
|
|
5903
|
+
const onExit = (code, signal) => {
|
|
5904
|
+
Deferred3.unsafeDone(termination, Effect11.succeed(code === null ? { _tag: "Signaled", signal } : { _tag: "Exited", code }));
|
|
5905
|
+
};
|
|
5906
|
+
const onSpawn = () => {
|
|
5907
|
+
acquired = true;
|
|
5908
|
+
resume(Effect11.succeed(handle));
|
|
5909
|
+
};
|
|
5910
|
+
handle.once("error", onError);
|
|
5911
|
+
handle.once("exit", onExit);
|
|
5912
|
+
handle.once("spawn", onSpawn);
|
|
5913
|
+
return Effect11.sync(() => {
|
|
5914
|
+
if (!acquired) {
|
|
5915
|
+
handle.removeListener("error", onError);
|
|
5916
|
+
handle.removeListener("exit", onExit);
|
|
5917
|
+
handle.removeListener("spawn", onSpawn);
|
|
5918
|
+
if (handle.exitCode === null && handle.signalCode === null) {
|
|
5919
|
+
try {
|
|
5920
|
+
handle.kill("SIGTERM");
|
|
5921
|
+
} catch {}
|
|
5922
|
+
}
|
|
5923
|
+
}
|
|
5924
|
+
});
|
|
5925
|
+
});
|
|
5926
|
+
return { child, termination };
|
|
5927
|
+
});
|
|
5928
|
+
var FORWARDED_SIGNALS = process.platform === "win32" ? ["SIGINT", "SIGTERM", "SIGBREAK"] : ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
5929
|
+
var awaitWithSignalForwarding = ({ child, termination }) => Effect11.acquireUseRelease(Effect11.sync(() => {
|
|
5930
|
+
const listeners = new Map;
|
|
5931
|
+
for (const signal of FORWARDED_SIGNALS) {
|
|
5932
|
+
const listener = () => {
|
|
5933
|
+
try {
|
|
5934
|
+
child.kill(signal);
|
|
5935
|
+
} catch {}
|
|
5936
|
+
};
|
|
5937
|
+
listeners.set(signal, listener);
|
|
5938
|
+
process.on(signal, listener);
|
|
5939
|
+
}
|
|
5940
|
+
return listeners;
|
|
5941
|
+
}), () => Deferred3.await(termination), (listeners) => Effect11.sync(() => {
|
|
5942
|
+
for (const [signal, listener] of listeners) {
|
|
5943
|
+
process.removeListener(signal, listener);
|
|
5944
|
+
}
|
|
5945
|
+
}));
|
|
5946
|
+
var terminateAndReap = ({ child, termination }) => Effect11.gen(function* () {
|
|
5947
|
+
if (yield* Deferred3.isDone(termination))
|
|
5948
|
+
return;
|
|
5949
|
+
yield* Effect11.sync(() => {
|
|
5950
|
+
try {
|
|
5951
|
+
child.kill("SIGTERM");
|
|
5952
|
+
} catch {}
|
|
5953
|
+
});
|
|
5954
|
+
const graceful = yield* Deferred3.await(termination).pipe(Effect11.ignore, Effect11.timeoutOption(Duration2.seconds(2)));
|
|
5955
|
+
if (Option17.isSome(graceful))
|
|
5956
|
+
return;
|
|
5957
|
+
yield* Effect11.sync(() => {
|
|
5958
|
+
try {
|
|
5959
|
+
child.kill("SIGKILL");
|
|
5960
|
+
} catch {}
|
|
5961
|
+
});
|
|
5962
|
+
yield* Deferred3.await(termination).pipe(Effect11.ignore, Effect11.timeout(Duration2.seconds(2)), Effect11.ignore);
|
|
5963
|
+
});
|
|
5964
|
+
var runInteractiveProcess = (input) => Effect11.acquireUseRelease(acquireInteractiveProcess(input), awaitWithSignalForwarding, terminateAndReap);
|
|
5965
|
+
// ../../packages/client-common/src/harness-connections/hosted-setup.ts
|
|
5966
|
+
import { Schema as Schema52 } from "effect";
|
|
5967
|
+
var HOSTED_SETUP_PROTOCOL_VERSION = 1;
|
|
5968
|
+
var HOSTED_SETUP_MAX_RESULT_BYTES = 16 * 1024;
|
|
5969
|
+
var HostedSetupCapability = Schema52.Struct({
|
|
5970
|
+
protocolVersion: Schema52.Literal(HOSTED_SETUP_PROTOCOL_VERSION)
|
|
5971
|
+
});
|
|
5972
|
+
var version = Schema52.Literal(HOSTED_SETUP_PROTOCOL_VERSION);
|
|
5973
|
+
var HostedSetupResult = Schema52.Union(Schema52.TaggedStruct("Completed", { protocolVersion: version, modelId: ModelIdSchema }), Schema52.TaggedStruct("Cancelled", { protocolVersion: version }), Schema52.TaggedStruct("Failed", {
|
|
5974
|
+
protocolVersion: version,
|
|
5975
|
+
message: Schema52.String.pipe(Schema52.filter((message) => new TextEncoder().encode(message).length <= 4096))
|
|
5976
|
+
}));
|
|
5977
|
+
|
|
5978
|
+
// extensions/setup.ts
|
|
5979
|
+
class PiSetupFailed extends Schema53.TaggedError()("PiSetupFailed", {
|
|
5980
|
+
message: Schema53.String
|
|
5981
|
+
}) {
|
|
5982
|
+
}
|
|
5983
|
+
var failure = (message) => new PiSetupFailed({ message });
|
|
5984
|
+
var prepareMagnitudeCli = (cwd, setMessage = () => Effect12.void) => Effect12.gen(function* () {
|
|
5985
|
+
const fs = yield* FileSystem.FileSystem;
|
|
5986
|
+
const override = process.env.MAGNITUDE_CLI?.trim();
|
|
5987
|
+
const executable = override || "magnitude";
|
|
5988
|
+
const probe = Effect12.scoped(Effect12.gen(function* () {
|
|
5989
|
+
const child = yield* Command2.make(executable, "setup", "--host-protocol").pipe(Command2.start);
|
|
5990
|
+
const [code, output, stderr] = yield* Effect12.all([
|
|
5991
|
+
child.exitCode,
|
|
5992
|
+
child.stdout.pipe(Stream7.decodeText(), Stream7.runFold("", (text2, part) => (text2 + part).slice(0, 4097))),
|
|
5993
|
+
child.stderr.pipe(Stream7.decodeText(), Stream7.runFold("", (text2, part) => (text2 + part).slice(-4096)))
|
|
5994
|
+
], { concurrency: "unbounded" });
|
|
5995
|
+
if (code !== 0 || output.length > 4096)
|
|
5996
|
+
return yield* failure(`Magnitude's setup capability check failed at ${executable} (exit ${code}). ${stripVTControlCharacters(stderr).trim().slice(-600)}${override ? " Check MAGNITUDE_CLI." : ""}`);
|
|
5997
|
+
return output;
|
|
5998
|
+
}));
|
|
5999
|
+
const capability = yield* probe.pipe(Effect12.catchIf((error) => !override && error._tag === "SystemError" && error.reason === "NotFound", (error) => Effect12.gen(function* () {
|
|
6000
|
+
for (const directory of (process.env.PATH ?? "").split(delimiter)) {
|
|
6001
|
+
if (yield* fs.exists(join(directory || cwd, executable)))
|
|
6002
|
+
return yield* error;
|
|
6003
|
+
}
|
|
6004
|
+
yield* setMessage("Installing Magnitude…");
|
|
6005
|
+
yield* Effect12.scoped(Effect12.gen(function* () {
|
|
6006
|
+
const child = yield* Command2.make("npm", "install", "--global", "@magnitudedev/cli").pipe(Command2.workingDirectory(cwd), Command2.start);
|
|
6007
|
+
const [code, , stderr] = yield* Effect12.all([
|
|
6008
|
+
child.exitCode,
|
|
6009
|
+
Stream7.runDrain(child.stdout),
|
|
6010
|
+
child.stderr.pipe(Stream7.decodeText(), Stream7.runFold("", (text2, part) => (text2 + part).slice(-4096)))
|
|
6011
|
+
], { concurrency: "unbounded" });
|
|
6012
|
+
if (code !== 0)
|
|
6013
|
+
return yield* failure(`Magnitude installation failed (exit ${code}). ${stripVTControlCharacters(stderr).trim().slice(-600)} Run /magnitude-setup to retry.`);
|
|
6014
|
+
}));
|
|
6015
|
+
return yield* probe;
|
|
6016
|
+
})), Effect12.timeout("10 minutes"), Effect12.mapError((error) => error instanceof PiSetupFailed ? error : failure(`Could not prepare Magnitude: ${String(error)}. Run /magnitude-setup to retry${override ? ", or check MAGNITUDE_CLI" : ""}.`)));
|
|
6017
|
+
yield* Schema53.decodeUnknown(Schema53.parseJson(HostedSetupCapability))(capability).pipe(Effect12.mapError(() => failure(`The Magnitude CLI at ${executable} does not support hosted setup. Update @magnitudedev/cli${override ? ", or check MAGNITUDE_CLI" : ""}.`)));
|
|
6018
|
+
return executable;
|
|
6019
|
+
});
|
|
6020
|
+
var withPiPreparation = (ctx, work) => Effect12.acquireUseRelease(Effect12.async((resume, signal) => {
|
|
6021
|
+
let closed;
|
|
6022
|
+
closed = ctx.ui.custom((tui, theme, _keys, done) => {
|
|
6023
|
+
const loader = new CancellableLoader(tui, (text2) => theme.fg("accent", text2), (text2) => theme.fg("muted", text2), "Installing Magnitude…");
|
|
6024
|
+
if (signal.aborted) {
|
|
6025
|
+
loader.dispose();
|
|
6026
|
+
done();
|
|
6027
|
+
} else
|
|
6028
|
+
resume(Effect12.succeed({ loader, finish: () => done(), closed: () => closed }));
|
|
6029
|
+
return loader;
|
|
6030
|
+
});
|
|
6031
|
+
closed.catch((error) => resume(Effect12.fail(failure(`Could not open Pi setup: ${String(error)}`))));
|
|
6032
|
+
}), ({ loader }) => work((message) => Effect12.sync(() => loader.setMessage(message))).pipe(Effect12.raceFirst(Effect12.async((resume) => {
|
|
6033
|
+
loader.onAbort = () => resume(Effect12.fail(failure("Magnitude setup cancelled. Run /magnitude-setup to retry.")));
|
|
6034
|
+
if (loader.signal.aborted)
|
|
6035
|
+
loader.onAbort();
|
|
6036
|
+
return Effect12.sync(() => {
|
|
6037
|
+
loader.onAbort = undefined;
|
|
6038
|
+
});
|
|
6039
|
+
}))), ({ loader, finish, closed }) => Effect12.sync(() => {
|
|
6040
|
+
loader.dispose();
|
|
6041
|
+
finish();
|
|
6042
|
+
}).pipe(Effect12.zipRight(Effect12.promise(closed))));
|
|
6043
|
+
var restoreHostTerminal = (tui) => tui.terminal.write("\x1B[?2026l\x1B[?1003l\x1B[?1002l\x1B[?1000l\x1B[?1006l\x1B[?1004l" + "\x1B[?2004l\x1B[>4;0m\x1B[=0u\x1B[?1049l\x1B[0m\x1B[0 q");
|
|
6044
|
+
var withPiTerminal = (ctx, work) => Effect12.acquireUseRelease(Effect12.async((resume, signal) => {
|
|
6045
|
+
let closed;
|
|
6046
|
+
closed = ctx.ui.custom((tui, _theme, _keys, done) => {
|
|
6047
|
+
if (signal.aborted)
|
|
6048
|
+
done();
|
|
6049
|
+
else
|
|
6050
|
+
resume(Effect12.succeed({ tui, finish: () => done(), closed: () => closed }));
|
|
6051
|
+
return { render: () => [], invalidate: () => {} };
|
|
6052
|
+
});
|
|
6053
|
+
closed.catch((error) => resume(Effect12.fail(failure(`Could not open Pi setup: ${String(error)}`))));
|
|
6054
|
+
}), ({ tui }) => Effect12.sync(() => tui.stop()).pipe(Effect12.zipRight(work)), ({ tui, finish, closed }) => Effect12.sync(() => {
|
|
6055
|
+
try {
|
|
6056
|
+
restoreHostTerminal(tui);
|
|
6057
|
+
tui.start();
|
|
6058
|
+
tui.requestRender(true);
|
|
6059
|
+
} finally {
|
|
6060
|
+
finish();
|
|
6061
|
+
}
|
|
6062
|
+
}).pipe(Effect12.zipRight(Effect12.promise(closed))));
|
|
6063
|
+
var validateSetupTermination = (result, termination) => {
|
|
6064
|
+
if (termination._tag !== "Exited" || termination.code === 0 !== (result._tag !== "Failed")) {
|
|
6065
|
+
return Effect12.fail(failure("Magnitude setup exited without a consistent completion result. Run /magnitude-setup to retry."));
|
|
6066
|
+
}
|
|
6067
|
+
return result._tag === "Failed" ? Effect12.fail(failure(result.message)) : Effect12.succeed(result);
|
|
6068
|
+
};
|
|
6069
|
+
var PiSetup = Context7.GenericTag("pi/PiSetup");
|
|
6070
|
+
var PiSetupLive = Layer6.effect(PiSetup, Effect12.gen(function* () {
|
|
6071
|
+
const fs = yield* FileSystem.FileSystem;
|
|
6072
|
+
const executor = yield* CommandExecutor3.CommandExecutor;
|
|
6073
|
+
return {
|
|
6074
|
+
run: (ctx) => Effect12.scoped(Effect12.gen(function* () {
|
|
6075
|
+
const directory = yield* fs.makeTempDirectoryScoped({ prefix: "magnitude-pi-setup-" });
|
|
6076
|
+
const resultPath = `${directory}/result.json`;
|
|
6077
|
+
const executable = yield* withPiPreparation(ctx, (setMessage) => prepareMagnitudeCli(ctx.cwd, setMessage));
|
|
6078
|
+
const termination = yield* withPiTerminal(ctx, runInteractiveProcess({
|
|
6079
|
+
executable,
|
|
6080
|
+
args: ["setup", "--host", "pi", "--result-file", resultPath],
|
|
6081
|
+
environment: process.env,
|
|
6082
|
+
workingDirectory: ctx.cwd
|
|
6083
|
+
}));
|
|
6084
|
+
if (termination._tag === "Signaled" && termination.signal === "SIGINT") {
|
|
6085
|
+
return { protocolVersion: HOSTED_SETUP_PROTOCOL_VERSION, _tag: "Cancelled" };
|
|
6086
|
+
}
|
|
6087
|
+
if (termination._tag === "Signaled") {
|
|
6088
|
+
return yield* failure(`Magnitude setup stopped unexpectedly (${termination.signal}). Run /magnitude-setup to retry.`);
|
|
6089
|
+
}
|
|
6090
|
+
const stat = yield* fs.stat(resultPath).pipe(Effect12.mapError(() => failure("Magnitude setup exited without a completion result. Run /magnitude-setup to retry.")));
|
|
6091
|
+
if (stat.type !== "File" || stat.size > BigInt(HOSTED_SETUP_MAX_RESULT_BYTES)) {
|
|
6092
|
+
return yield* failure("Magnitude setup returned an invalid or oversized result");
|
|
6093
|
+
}
|
|
6094
|
+
const result = yield* fs.readFileString(resultPath).pipe(Effect12.flatMap(Schema53.decodeUnknown(Schema53.parseJson(HostedSetupResult))), Effect12.mapError(() => failure("Magnitude setup did not return a valid completion result. Run /magnitude-setup to retry.")));
|
|
6095
|
+
return yield* validateSetupTermination(result, termination);
|
|
6096
|
+
})).pipe(Effect12.provideService(CommandExecutor3.CommandExecutor, executor), Effect12.provideService(FileSystem.FileSystem, fs), Effect12.mapError((error) => error instanceof PiSetupFailed ? error : failure(`Magnitude setup could not finish: ${String(error)}`)))
|
|
6097
|
+
};
|
|
6098
|
+
}));
|
|
6099
|
+
var registerMagnitudeSetup = (pi, layer4 = PiSetupLive.pipe(Layer6.provide(NodeContext.layer))) => {
|
|
6100
|
+
const runtime = ManagedRuntime2.make(layer4);
|
|
6101
|
+
const scope = Effect12.runSync(Scope4.make());
|
|
6102
|
+
const gate = Effect12.runSync(Effect12.makeSemaphore(1));
|
|
6103
|
+
const action = (ctx) => Effect12.gen(function* () {
|
|
6104
|
+
if (ctx.mode !== "tui")
|
|
6105
|
+
return yield* failure("Run /magnitude-setup in Pi's interactive terminal.");
|
|
6106
|
+
if (!ctx.isIdle() || ctx.hasPendingMessages())
|
|
6107
|
+
return yield* failure("Wait for the current task to finish, then run /magnitude-setup.");
|
|
6108
|
+
const setup = yield* PiSetup;
|
|
6109
|
+
const result = yield* setup.run(ctx);
|
|
6110
|
+
if (result._tag !== "Completed")
|
|
6111
|
+
return false;
|
|
6112
|
+
yield* Effect12.tryPromise({
|
|
6113
|
+
try: async () => {
|
|
6114
|
+
await ctx.modelRegistry.refresh();
|
|
6115
|
+
const model2 = ctx.modelRegistry.find("magnitude", result.modelId);
|
|
6116
|
+
if (!model2 || !await pi.setModel(model2))
|
|
6117
|
+
throw new Error("The selected model is not available in Pi");
|
|
6118
|
+
},
|
|
6119
|
+
catch: (error) => failure(`Magnitude setup completed, but Pi could not activate the model: ${String(error)}. Run /reload and select it with /model.`)
|
|
6120
|
+
});
|
|
6121
|
+
return true;
|
|
6122
|
+
});
|
|
6123
|
+
const run = (ctx) => runtime.runPromise(Effect12.forkIn(gate.withPermitsIfAvailable(1)(action(ctx)).pipe(Effect12.flatMap((result) => result._tag === "Some" ? Effect12.succeed(result.value) : Effect12.fail(failure("Magnitude setup is already open."))), Effect12.catchAll((error) => Effect12.sync(() => {
|
|
6124
|
+
ctx.ui.notify(error.message, "error");
|
|
6125
|
+
return false;
|
|
6126
|
+
}))), scope).pipe(Effect12.flatMap(Fiber2.join)));
|
|
6127
|
+
pi.registerCommand("magnitude-setup", {
|
|
6128
|
+
description: "Choose and set up a local Magnitude model",
|
|
6129
|
+
handler: async (_args, ctx) => {
|
|
6130
|
+
const reload = await run(ctx);
|
|
6131
|
+
if (reload) {
|
|
6132
|
+
try {
|
|
6133
|
+
await ctx.reload();
|
|
6134
|
+
} catch (error) {
|
|
6135
|
+
throw failure(`Magnitude setup completed and selected the model, but Pi could not reload its resources: ${String(error)}. Run /reload to retry.`);
|
|
6136
|
+
}
|
|
6137
|
+
}
|
|
6138
|
+
}
|
|
6139
|
+
});
|
|
6140
|
+
return {
|
|
6141
|
+
run,
|
|
6142
|
+
dispose: async () => {
|
|
6143
|
+
await Effect12.runPromise(Scope4.close(scope, Exit3.void));
|
|
6144
|
+
await runtime.dispose();
|
|
6145
|
+
}
|
|
6146
|
+
};
|
|
6147
|
+
};
|
|
6148
|
+
|
|
6149
|
+
// extensions/onboarding.ts
|
|
5851
6150
|
var SETUP_QUESTION = "Set up local models with Magnitude now?";
|
|
5852
6151
|
var SETUP_REMINDER = "You can set up local models anytime with `/magnitude-setup`.";
|
|
5853
|
-
var claimSetupOffer = (agentDir) =>
|
|
5854
|
-
const fs = yield*
|
|
6152
|
+
var claimSetupOffer = (agentDir) => Effect13.gen(function* () {
|
|
6153
|
+
const fs = yield* FileSystem3.FileSystem;
|
|
5855
6154
|
const directory = `${agentDir}/magnitude`;
|
|
5856
6155
|
yield* fs.makeDirectory(directory, { recursive: true });
|
|
5857
|
-
return yield* fs.writeFileString(`${directory}/setup-offered`, "", { flag: "wx" }).pipe(
|
|
6156
|
+
return yield* fs.writeFileString(`${directory}/setup-offered`, "", { flag: "wx" }).pipe(Effect13.as(true), Effect13.catchTag("SystemError", (error) => error.reason === "AlreadyExists" ? Effect13.succeed(false) : Effect13.fail(error)));
|
|
5858
6157
|
});
|
|
5859
6158
|
function canOfferSetup(ctx, argv) {
|
|
5860
6159
|
if (ctx.mode !== "tui" || !ctx.isIdle() || ctx.hasPendingMessages() || ctx.ui.getEditorText().trim())
|
|
@@ -5866,30 +6165,21 @@ function canOfferSetup(ctx, argv) {
|
|
|
5866
6165
|
const args = parseArgs([...argv]);
|
|
5867
6166
|
return args.messages.length === 0 && args.fileArgs.length === 0;
|
|
5868
6167
|
}
|
|
5869
|
-
var offerSetup = (pi, ctx, agentDir, signal) =>
|
|
6168
|
+
var offerSetup = (pi, ctx, agentDir, signal, runSetup) => Effect13.gen(function* () {
|
|
5870
6169
|
if (!(yield* claimSetupOffer(agentDir)) || signal.aborted)
|
|
5871
6170
|
return;
|
|
5872
|
-
const accepted = yield*
|
|
6171
|
+
const accepted = yield* Effect13.tryPromise(() => ctx.ui.confirm(SETUP_QUESTION, "", { signal }));
|
|
5873
6172
|
if (signal.aborted)
|
|
5874
6173
|
return;
|
|
5875
|
-
if (accepted && ctx.isIdle() && !ctx.hasPendingMessages())
|
|
5876
|
-
|
|
5877
|
-
else
|
|
6174
|
+
if (accepted && ctx.isIdle() && !ctx.hasPendingMessages()) {
|
|
6175
|
+
yield* Effect13.tryPromise(() => runSetup(ctx));
|
|
6176
|
+
} else
|
|
5878
6177
|
pi.sendMessage({ customType: "magnitude-setup", content: SETUP_REMINDER, display: true });
|
|
5879
6178
|
});
|
|
5880
6179
|
function registerMagnitudeOnboarding(pi) {
|
|
5881
6180
|
const abort = new AbortController;
|
|
5882
|
-
const scope =
|
|
5883
|
-
|
|
5884
|
-
description: "Set up local models with Magnitude",
|
|
5885
|
-
handler: async (_args, ctx) => {
|
|
5886
|
-
if (!ctx.isIdle() || ctx.hasPendingMessages()) {
|
|
5887
|
-
ctx.ui.notify("Wait for the current task to finish, then run /magnitude-setup.", "warning");
|
|
5888
|
-
return;
|
|
5889
|
-
}
|
|
5890
|
-
pi.sendUserMessage(SETUP_PROMPT);
|
|
5891
|
-
}
|
|
5892
|
-
});
|
|
6181
|
+
const scope = Effect13.runSync(Scope5.make());
|
|
6182
|
+
const setup = registerMagnitudeSetup(pi);
|
|
5893
6183
|
pi.on("resources_discover", () => {
|
|
5894
6184
|
if (parseArgs(process.argv.slice(2)).noSkills || pi.getCommands().some((command) => command.source === "skill" && command.name === "skill:magnitude"))
|
|
5895
6185
|
return;
|
|
@@ -5898,24 +6188,25 @@ function registerMagnitudeOnboarding(pi) {
|
|
|
5898
6188
|
pi.on("session_start", (event, ctx) => {
|
|
5899
6189
|
if (event.reason !== "startup" || !canOfferSetup(ctx, process.argv.slice(2)))
|
|
5900
6190
|
return;
|
|
5901
|
-
|
|
6191
|
+
Effect13.runFork(Effect13.forkIn(offerSetup(pi, ctx, getAgentDir(), abort.signal, setup.run).pipe(Effect13.provide(NodeFileSystem2.layer), Effect13.catchAll(() => Effect13.sync(() => ctx.ui.notify(`Magnitude setup could not open. ${SETUP_REMINDER}`, "warning")))), scope));
|
|
5902
6192
|
});
|
|
5903
|
-
return () => {
|
|
6193
|
+
return async () => {
|
|
5904
6194
|
abort.abort();
|
|
5905
|
-
|
|
6195
|
+
await Effect13.runPromise(Scope5.close(scope, Exit4.void));
|
|
6196
|
+
await setup.dispose();
|
|
5906
6197
|
};
|
|
5907
6198
|
}
|
|
5908
6199
|
|
|
5909
6200
|
// extensions/observing-fetch.ts
|
|
5910
|
-
import { Effect as
|
|
6201
|
+
import { Effect as Effect14, Either as Either3, Schema as Schema55 } from "effect";
|
|
5911
6202
|
|
|
5912
6203
|
// extensions/protocol.ts
|
|
5913
|
-
import { Either as Either2, Schema as
|
|
5914
|
-
var ProgressChunkSchema =
|
|
5915
|
-
var TimingsChunkSchema =
|
|
6204
|
+
import { Either as Either2, Schema as Schema54 } from "effect";
|
|
6205
|
+
var ProgressChunkSchema = Schema54.Struct({ progress: MagnitudeProgressSchema });
|
|
6206
|
+
var TimingsChunkSchema = Schema54.Struct({ timings: MagnitudeTimingsSchema });
|
|
5916
6207
|
var decodeMagnitudeObservation = (value) => {
|
|
5917
|
-
const progress =
|
|
5918
|
-
const timings =
|
|
6208
|
+
const progress = Schema54.decodeUnknownEither(ProgressChunkSchema)(value);
|
|
6209
|
+
const timings = Schema54.decodeUnknownEither(TimingsChunkSchema)(value);
|
|
5919
6210
|
if (Either2.isLeft(progress) && Either2.isLeft(timings))
|
|
5920
6211
|
return;
|
|
5921
6212
|
return {
|
|
@@ -5926,7 +6217,7 @@ var decodeMagnitudeObservation = (value) => {
|
|
|
5926
6217
|
|
|
5927
6218
|
// extensions/observing-fetch.ts
|
|
5928
6219
|
var MAGNITUDE_PROGRESS_HEADER = "Magnitude-Include-Progress";
|
|
5929
|
-
var JsonDocumentSchema =
|
|
6220
|
+
var JsonDocumentSchema = Schema55.parseJson(Schema55.Unknown);
|
|
5930
6221
|
|
|
5931
6222
|
class SseDataParser {
|
|
5932
6223
|
#buffer = "";
|
|
@@ -5973,26 +6264,26 @@ class SseDataParser {
|
|
|
5973
6264
|
this.#dataLines = [];
|
|
5974
6265
|
if (data === "[DONE]")
|
|
5975
6266
|
return;
|
|
5976
|
-
const decoded =
|
|
6267
|
+
const decoded = Schema55.decodeUnknownEither(JsonDocumentSchema)(data);
|
|
5977
6268
|
if (Either3.isRight(decoded))
|
|
5978
6269
|
observe(decoded.right);
|
|
5979
6270
|
}
|
|
5980
6271
|
}
|
|
5981
|
-
var observeResponse = (response, request, signal) =>
|
|
6272
|
+
var observeResponse = (response, request, signal) => Effect14.scoped(Effect14.gen(function* () {
|
|
5982
6273
|
if (!response.ok || response.body === null) {
|
|
5983
6274
|
yield* request.fail;
|
|
5984
6275
|
return;
|
|
5985
6276
|
}
|
|
5986
|
-
const reader = yield*
|
|
6277
|
+
const reader = yield* Effect14.acquireRelease(Effect14.sync(() => response.body.getReader()), (reader2) => Effect14.promise(() => reader2.cancel()).pipe(Effect14.interruptible, Effect14.timeout("100 millis"), Effect14.ignore, Effect14.ensuring(Effect14.sync(() => reader2.releaseLock()))));
|
|
5987
6278
|
if (signal.aborted) {
|
|
5988
6279
|
yield* request.fail;
|
|
5989
6280
|
return;
|
|
5990
6281
|
}
|
|
5991
6282
|
const decoder = new TextDecoder;
|
|
5992
6283
|
const parser = new SseDataParser;
|
|
5993
|
-
const drain =
|
|
6284
|
+
const drain = Effect14.gen(function* () {
|
|
5994
6285
|
while (true) {
|
|
5995
|
-
const result = yield*
|
|
6286
|
+
const result = yield* Effect14.tryPromise(() => reader.read());
|
|
5996
6287
|
if (result.done)
|
|
5997
6288
|
break;
|
|
5998
6289
|
const values = [];
|
|
@@ -6005,35 +6296,35 @@ var observeResponse = (response, request, signal) => Effect12.scoped(Effect12.ge
|
|
|
6005
6296
|
}
|
|
6006
6297
|
yield* request.finish;
|
|
6007
6298
|
});
|
|
6008
|
-
const aborted =
|
|
6009
|
-
const abort = () => resume(
|
|
6299
|
+
const aborted = Effect14.async((resume) => {
|
|
6300
|
+
const abort = () => resume(Effect14.interrupt);
|
|
6010
6301
|
signal.addEventListener("abort", abort, { once: true });
|
|
6011
6302
|
if (signal.aborted)
|
|
6012
6303
|
abort();
|
|
6013
|
-
return
|
|
6304
|
+
return Effect14.sync(() => signal.removeEventListener("abort", abort));
|
|
6014
6305
|
});
|
|
6015
|
-
yield*
|
|
6016
|
-
})).pipe(
|
|
6306
|
+
yield* Effect14.raceFirst(drain, aborted).pipe(Effect14.onError(() => request.fail));
|
|
6307
|
+
})).pipe(Effect14.catchAllCause(() => Effect14.void));
|
|
6017
6308
|
var makeObservingFetch = (fetchImplementation, begin, scope) => {
|
|
6018
6309
|
const observingFetch = async (input, init) => {
|
|
6019
6310
|
const request = new Request(input, init);
|
|
6020
6311
|
request.headers.set(MAGNITUDE_PROGRESS_HEADER, "true");
|
|
6021
|
-
const progress =
|
|
6022
|
-
observe: () =>
|
|
6023
|
-
finish:
|
|
6024
|
-
fail:
|
|
6312
|
+
const progress = Effect14.runSync(Effect14.suspend(begin).pipe(Effect14.catchAllCause(() => Effect14.succeed({
|
|
6313
|
+
observe: () => Effect14.void,
|
|
6314
|
+
finish: Effect14.void,
|
|
6315
|
+
fail: Effect14.void
|
|
6025
6316
|
}))));
|
|
6026
6317
|
try {
|
|
6027
6318
|
const response = await fetchImplementation(request);
|
|
6028
6319
|
try {
|
|
6029
6320
|
const observation = observeResponse(response.clone(), progress, request.signal);
|
|
6030
|
-
|
|
6321
|
+
Effect14.runFork(Effect14.forkIn(observation, scope));
|
|
6031
6322
|
} catch {
|
|
6032
|
-
|
|
6323
|
+
Effect14.runSync(progress.fail.pipe(Effect14.catchAllCause(() => Effect14.void)));
|
|
6033
6324
|
}
|
|
6034
6325
|
return response;
|
|
6035
6326
|
} catch (error) {
|
|
6036
|
-
|
|
6327
|
+
Effect14.runSync(progress.fail.pipe(Effect14.catchAllCause(() => Effect14.void)));
|
|
6037
6328
|
throw error;
|
|
6038
6329
|
}
|
|
6039
6330
|
};
|
|
@@ -6044,9 +6335,9 @@ var makeObservingFetch = (fetchImplementation, begin, scope) => {
|
|
|
6044
6335
|
|
|
6045
6336
|
// extensions/progress.ts
|
|
6046
6337
|
import { Text } from "@earendil-works/pi-tui";
|
|
6047
|
-
import { Data as Data3, Effect as
|
|
6338
|
+
import { Data as Data3, Effect as Effect15, Fiber as Fiber3, Option as Option18, Queue, Schema as Schema56 } from "effect";
|
|
6048
6339
|
var MAGNITUDE_SUMMARY_WIDGET_KEY = "magnitude-inference-summary";
|
|
6049
|
-
var RequestId =
|
|
6340
|
+
var RequestId = Schema56.Number.pipe(Schema56.int(), Schema56.brand("ProgressRequestId"));
|
|
6050
6341
|
|
|
6051
6342
|
class Observing extends Data3.TaggedClass("Observing") {
|
|
6052
6343
|
}
|
|
@@ -6101,31 +6392,31 @@ var formatLiveProgress = ({ progress, modelName, startedAt }, now) => {
|
|
|
6101
6392
|
}
|
|
6102
6393
|
}
|
|
6103
6394
|
};
|
|
6104
|
-
var makeProgressTracker = (ui, now = () => performance.now()) =>
|
|
6395
|
+
var makeProgressTracker = (ui, now = () => performance.now()) => Effect15.gen(function* () {
|
|
6105
6396
|
let state = new Idle2;
|
|
6106
6397
|
let nextId = 0;
|
|
6107
6398
|
let active;
|
|
6108
6399
|
let latest;
|
|
6109
6400
|
let nextResponseId = 0;
|
|
6110
6401
|
const wake = yield* Queue.sliding(1);
|
|
6111
|
-
const present = (f) =>
|
|
6402
|
+
const present = (f) => Effect15.sync(f).pipe(Effect15.catchAllCause(() => Effect15.void));
|
|
6112
6403
|
const clearSummary = present(() => ui.setWidget(MAGNITUDE_SUMMARY_WIDGET_KEY, undefined));
|
|
6113
6404
|
const resetRow = present(() => ui.setWorkingMessage());
|
|
6114
6405
|
const render2 = present(() => {
|
|
6115
6406
|
if (active)
|
|
6116
6407
|
ui.setWorkingMessage(formatLiveProgress(active.phase, now()));
|
|
6117
6408
|
});
|
|
6118
|
-
const finalize =
|
|
6409
|
+
const finalize = Effect15.gen(function* () {
|
|
6119
6410
|
if (state._tag !== "Settled")
|
|
6120
6411
|
return;
|
|
6121
|
-
if ([...state.run.responses.values()].some(
|
|
6412
|
+
if ([...state.run.responses.values()].some(Option18.isNone))
|
|
6122
6413
|
return;
|
|
6123
6414
|
if ([...state.run.requests.values()].some((r) => r._tag === "Observing"))
|
|
6124
6415
|
return;
|
|
6125
6416
|
const settled = state;
|
|
6126
6417
|
state = runMachine.transition(settled, "Idle", {});
|
|
6127
6418
|
const timings = [...settled.run.completed.entries()].filter(([id]) => settled.run.accepted.has(id)).sort(([a], [b]) => a - b).map(([, value]) => value);
|
|
6128
|
-
if ([...settled.run.responses.values()].some((outcome) => !
|
|
6419
|
+
if ([...settled.run.responses.values()].some((outcome) => !Option18.getOrElse(outcome, () => false)) || timings.length === 0) {
|
|
6129
6420
|
yield* clearSummary;
|
|
6130
6421
|
return;
|
|
6131
6422
|
}
|
|
@@ -6134,7 +6425,7 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect13.gen(fu
|
|
|
6134
6425
|
const summary = `● ${settled.run.modelName} worked for ${duration(settled.settledAt - settled.run.startedAt)}` + ` · ${seconds(timings[0].time_to_first_token_ms)} TTFT` + (decodeMs > 0 && tokens > 0 ? ` · ${(tokens * 1000 / decodeMs).toFixed(1)} tok/s` : "");
|
|
6135
6426
|
yield* present(() => ui.setWidget(MAGNITUDE_SUMMARY_WIDGET_KEY, (_tui, theme) => new Text(theme.fg("muted", summary), 0, 0)));
|
|
6136
6427
|
});
|
|
6137
|
-
const clear =
|
|
6428
|
+
const clear = Effect15.gen(function* () {
|
|
6138
6429
|
if (state._tag === "Disposed")
|
|
6139
6430
|
return;
|
|
6140
6431
|
if (state._tag !== "Idle")
|
|
@@ -6144,7 +6435,7 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect13.gen(fu
|
|
|
6144
6435
|
yield* resetRow;
|
|
6145
6436
|
yield* clearSummary;
|
|
6146
6437
|
});
|
|
6147
|
-
const startRun = (modelName) =>
|
|
6438
|
+
const startRun = (modelName) => Effect15.gen(function* () {
|
|
6148
6439
|
if (state._tag === "Disposed")
|
|
6149
6440
|
return;
|
|
6150
6441
|
if (state._tag === "Settled")
|
|
@@ -6154,38 +6445,38 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect13.gen(fu
|
|
|
6154
6445
|
state = runMachine.transition(state, "Working", { run: { startedAt: now(), modelName, requests: new Map, completed: new Map, responses: new Map, accepted: new Set } });
|
|
6155
6446
|
yield* clearSummary;
|
|
6156
6447
|
});
|
|
6157
|
-
const timer = yield*
|
|
6448
|
+
const timer = yield* Effect15.forever(Effect15.gen(function* () {
|
|
6158
6449
|
yield* Queue.take(wake);
|
|
6159
6450
|
while (active) {
|
|
6160
|
-
yield*
|
|
6451
|
+
yield* Effect15.sleep("100 millis");
|
|
6161
6452
|
yield* render2;
|
|
6162
6453
|
}
|
|
6163
|
-
})).pipe(
|
|
6164
|
-
yield*
|
|
6454
|
+
})).pipe(Effect15.forkScoped);
|
|
6455
|
+
yield* Effect15.addFinalizer(() => Effect15.gen(function* () {
|
|
6165
6456
|
yield* clear;
|
|
6166
6457
|
if (state._tag !== "Disposed")
|
|
6167
6458
|
state = runMachine.transition(state, "Disposed", {});
|
|
6168
|
-
yield*
|
|
6459
|
+
yield* Fiber3.interrupt(timer);
|
|
6169
6460
|
}));
|
|
6170
6461
|
return {
|
|
6171
6462
|
startRun,
|
|
6172
6463
|
clear,
|
|
6173
|
-
settleRun:
|
|
6464
|
+
settleRun: Effect15.gen(function* () {
|
|
6174
6465
|
if (state._tag === "Working")
|
|
6175
6466
|
state = runMachine.transition(state, "Settled", { settledAt: now() });
|
|
6176
6467
|
yield* finalize;
|
|
6177
6468
|
}),
|
|
6178
|
-
beginResponse: (modelName) =>
|
|
6469
|
+
beginResponse: (modelName) => Effect15.gen(function* () {
|
|
6179
6470
|
yield* startRun(modelName);
|
|
6180
6471
|
const run = state._tag === "Working" ? state.run : undefined;
|
|
6181
6472
|
const responseId = ++nextResponseId;
|
|
6182
6473
|
const requests = [];
|
|
6183
|
-
run?.responses.set(responseId,
|
|
6474
|
+
run?.responses.set(responseId, Option18.none());
|
|
6184
6475
|
const belongs = () => run !== undefined && (state._tag === "Working" || state._tag === "Settled") && state.run === run;
|
|
6185
|
-
const end = (successful) =>
|
|
6186
|
-
if (!belongs() ||
|
|
6476
|
+
const end = (successful) => Effect15.gen(function* () {
|
|
6477
|
+
if (!belongs() || Option18.isSome(run.responses.get(responseId)))
|
|
6187
6478
|
return;
|
|
6188
|
-
run.responses.set(responseId,
|
|
6479
|
+
run.responses.set(responseId, Option18.some(successful));
|
|
6189
6480
|
const last = requests.at(-1);
|
|
6190
6481
|
if (successful && last !== undefined)
|
|
6191
6482
|
run.accepted.add(last);
|
|
@@ -6204,16 +6495,16 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect13.gen(fu
|
|
|
6204
6495
|
}
|
|
6205
6496
|
yield* finalize;
|
|
6206
6497
|
});
|
|
6207
|
-
const begin =
|
|
6208
|
-
if (!belongs() ||
|
|
6209
|
-
return { observe: () =>
|
|
6498
|
+
const begin = Effect15.gen(function* () {
|
|
6499
|
+
if (!belongs() || Option18.isSome(run.responses.get(responseId)))
|
|
6500
|
+
return { observe: () => Effect15.void, finish: Effect15.void, fail: Effect15.void };
|
|
6210
6501
|
const id = RequestId.make(++nextId);
|
|
6211
6502
|
requests.push(id);
|
|
6212
6503
|
latest = id;
|
|
6213
6504
|
active = undefined;
|
|
6214
|
-
run?.requests.set(id, new Observing({ timings:
|
|
6505
|
+
run?.requests.set(id, new Observing({ timings: Option18.none() }));
|
|
6215
6506
|
yield* resetRow;
|
|
6216
|
-
const close = (failed) =>
|
|
6507
|
+
const close = (failed) => Effect15.gen(function* () {
|
|
6217
6508
|
if (!belongs())
|
|
6218
6509
|
return;
|
|
6219
6510
|
const request = run.requests.get(id);
|
|
@@ -6223,7 +6514,7 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect13.gen(fu
|
|
|
6223
6514
|
run.requests.set(id, requestMachine.transition(request, "Closed", {}));
|
|
6224
6515
|
else {
|
|
6225
6516
|
run.requests.set(id, requestMachine.transition(request, "Observed", {}));
|
|
6226
|
-
if (
|
|
6517
|
+
if (Option18.isSome(request.timings))
|
|
6227
6518
|
run.completed.set(id, request.timings.value);
|
|
6228
6519
|
}
|
|
6229
6520
|
if (latest === id) {
|
|
@@ -6233,14 +6524,14 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect13.gen(fu
|
|
|
6233
6524
|
yield* finalize;
|
|
6234
6525
|
});
|
|
6235
6526
|
return {
|
|
6236
|
-
observe: (observation) =>
|
|
6527
|
+
observe: (observation) => Effect15.gen(function* () {
|
|
6237
6528
|
if (!belongs())
|
|
6238
6529
|
return;
|
|
6239
6530
|
const request = run.requests.get(id);
|
|
6240
6531
|
if (request._tag !== "Observing")
|
|
6241
6532
|
return;
|
|
6242
6533
|
if (observation.timings)
|
|
6243
|
-
run.requests.set(id, requestMachine.hold(request, { timings:
|
|
6534
|
+
run.requests.set(id, requestMachine.hold(request, { timings: Option18.some(observation.timings) }));
|
|
6244
6535
|
if (latest !== id || !observation.progress)
|
|
6245
6536
|
return;
|
|
6246
6537
|
const progress = observation.progress;
|
|
@@ -6264,35 +6555,35 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect13.gen(fu
|
|
|
6264
6555
|
});
|
|
6265
6556
|
|
|
6266
6557
|
// extensions/magnitude.ts
|
|
6267
|
-
import { Effect as
|
|
6558
|
+
import { Effect as Effect16, Exit as Exit5, Scope as Scope7 } from "effect";
|
|
6268
6559
|
function magnitudeExtension(pi) {
|
|
6269
6560
|
const completions = openAICompletionsApi();
|
|
6270
6561
|
let tracker;
|
|
6271
6562
|
let scope;
|
|
6272
6563
|
const disposeCommands = registerMagnitudeCommands(pi);
|
|
6273
6564
|
const disposeOnboarding = registerMagnitudeOnboarding(pi);
|
|
6274
|
-
const perform = (effect) =>
|
|
6565
|
+
const perform = (effect) => Effect16.runSync((effect ?? Effect16.void).pipe(Effect16.catchAllCause(() => Effect16.void)));
|
|
6275
6566
|
pi.registerProvider("magnitude", {
|
|
6276
6567
|
api: "openai-completions",
|
|
6277
6568
|
streamSimple: (model2, context, options) => {
|
|
6278
6569
|
if (model2.api !== "openai-completions") {
|
|
6279
6570
|
throw new Error(`Magnitude for Pi requires openai-completions, received ${model2.api}`);
|
|
6280
6571
|
}
|
|
6281
|
-
const response = tracker &&
|
|
6572
|
+
const response = tracker && Effect16.runSync(tracker.beginResponse(model2.name));
|
|
6282
6573
|
const stream = completions.streamSimple(model2, context, {
|
|
6283
6574
|
...options,
|
|
6284
|
-
fetch: scope ? makeObservingFetch(options?.fetch ?? globalThis.fetch, () => response?.begin ??
|
|
6575
|
+
fetch: scope ? makeObservingFetch(options?.fetch ?? globalThis.fetch, () => response?.begin ?? Effect16.succeed({ observe: () => Effect16.void, finish: Effect16.void, fail: Effect16.void }), scope) : options?.fetch ?? globalThis.fetch
|
|
6285
6576
|
});
|
|
6286
6577
|
if (response && scope)
|
|
6287
|
-
|
|
6578
|
+
Effect16.runFork(Effect16.forkIn(Effect16.tryPromise(() => stream.result()).pipe(Effect16.flatMap((message) => response.end(message.stopReason !== "error" && message.stopReason !== "aborted")), Effect16.catchAllCause(() => response.end(false))), scope));
|
|
6288
6579
|
return stream;
|
|
6289
6580
|
}
|
|
6290
6581
|
});
|
|
6291
6582
|
pi.on("session_start", async (_event, ctx) => {
|
|
6292
6583
|
if (scope)
|
|
6293
|
-
await
|
|
6294
|
-
scope =
|
|
6295
|
-
tracker = await
|
|
6584
|
+
await Effect16.runPromise(Scope7.close(scope, Exit5.void));
|
|
6585
|
+
scope = Effect16.runSync(Scope7.make());
|
|
6586
|
+
tracker = await Effect16.runPromise(makeProgressTracker(ctx.ui).pipe(Scope7.extend(scope)));
|
|
6296
6587
|
});
|
|
6297
6588
|
pi.on("model_select", (event) => {
|
|
6298
6589
|
if (event.model.provider !== "magnitude")
|
|
@@ -6306,7 +6597,7 @@ function magnitudeExtension(pi) {
|
|
|
6306
6597
|
pi.on("session_shutdown", async () => {
|
|
6307
6598
|
await disposeOnboarding();
|
|
6308
6599
|
if (scope)
|
|
6309
|
-
await
|
|
6600
|
+
await Effect16.runPromise(Scope7.close(scope, Exit5.void));
|
|
6310
6601
|
tracker = undefined;
|
|
6311
6602
|
scope = undefined;
|
|
6312
6603
|
await disposeCommands();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magnitudedev/pi-extension",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.2",
|
|
4
4
|
"description": "Magnitude model controls and inference progress for Pi",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"@earendil-works/pi-tui": ">=0.83.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
+
"@magnitudedev/client-common": "workspace:*",
|
|
31
32
|
"@effect/platform-bun": "^0.90.0",
|
|
32
33
|
"@magnitudedev/release": "workspace:*",
|
|
33
34
|
"@magnitudedev/utils": "workspace:*",
|