@magnitudedev/pi-extension 0.0.1-alpha.1 → 0.0.1-alpha.3
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 +27 -7
- package/dist/magnitude-plugin.json +4 -4
- package/dist/magnitude.js +362 -86
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -10,9 +10,16 @@ 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. The extension invokes `magnitude setup --host pi`; `--host` is hidden from help.
|
|
21
|
+
If an older CLI does not recognize that option, update it explicitly and retry; it is never silently
|
|
22
|
+
replaced.
|
|
16
23
|
|
|
17
24
|
No or Escape leaves “You can set up local models anytime with `/magnitude-setup`.” in the conversation.
|
|
18
25
|
The offer is remembered per Pi profile across restarts, reloads, and package updates. Existing
|
|
@@ -20,9 +27,9 @@ Magnitude model configurations, non-interactive modes, conversations, and startu
|
|
|
20
27
|
alone. Run `/magnitude-setup` whenever you want to start onboarding yourself.
|
|
21
28
|
|
|
22
29
|
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
|
-
|
|
30
|
+
precedence without a collision warning. `--no-skills` disables this fallback too. No separate CLI
|
|
31
|
+
installation is needed. npm must be available with a writable global prefix, as for a normal npm
|
|
32
|
+
CLI installation. Merely loading the extension does not install software.
|
|
26
33
|
|
|
27
34
|
If you already have Magnitude and a local model, connect directly:
|
|
28
35
|
|
|
@@ -49,7 +56,7 @@ latency, and token-weighted generation throughput. Pi extensions execute with yo
|
|
|
49
56
|
|
|
50
57
|
Commands:
|
|
51
58
|
|
|
52
|
-
- `/magnitude-setup` —
|
|
59
|
+
- `/magnitude-setup` — open Magnitude's graphical model setup inside Pi
|
|
53
60
|
- `/load-model [model-id]` — load an installed model
|
|
54
61
|
- `/stop-model` — stop the active model
|
|
55
62
|
|
|
@@ -74,3 +81,16 @@ builds and runs the checkout's inference runtime,
|
|
|
74
81
|
and keeps the current source CLI available until Pi exits. Exiting Pi stops the development runtime
|
|
75
82
|
and restores the service state that existed before launch. The launcher inherits the current
|
|
76
83
|
environment; it does not start or configure tracing.
|
|
84
|
+
|
|
85
|
+
To test the first-run setup offer with no preconfigured Magnitude provider:
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
bun run dev:pi --setup
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
This installs only the checkout's package into the temporary Pi profile. Accept the setup offer,
|
|
92
|
+
choose a model, and return to Pi to chat. Both development modes keep connection files and skills
|
|
93
|
+
inside the temporary profile, and do not change login-startup registration. They share the machine's
|
|
94
|
+
real model storage and inference service: downloads consume disk space and selected models use
|
|
95
|
+
memory. The launcher restores the prior managed service after Pi exits. Ordinary installed setup
|
|
96
|
+
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.3",
|
|
4
4
|
"rpcVersion": 1,
|
|
5
|
-
"contentFingerprint": "
|
|
5
|
+
"contentFingerprint": "5d348e366e4c95b29edba024aae7756e7e941695537923bd3e82b1ddc54b9566",
|
|
6
6
|
"files": {
|
|
7
|
-
"README.md": "
|
|
8
|
-
"dist/magnitude.js": "
|
|
7
|
+
"README.md": "c4536c32bc79ddaee7d09536f79c845c2189e8ddc721e583836eb399d9322053",
|
|
8
|
+
"dist/magnitude.js": "14723faa27101270992397f22fcef2ca0f8cd55753e717250f0c92f921653e07",
|
|
9
9
|
"dist/skills/magnitude/SKILL.md": "37de67f0c7b831415f6298c8785b7ae97d5cfe454dc778651d96114840d2ace2"
|
|
10
10
|
}
|
|
11
11
|
}
|
package/dist/magnitude.js
CHANGED
|
@@ -5842,19 +5842,303 @@ 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 { FetchHttpClient as FetchHttpClient2 } from "@effect/platform";
|
|
5857
|
+
import { delimiter, join } from "node:path";
|
|
5858
|
+
import { stripVTControlCharacters } from "node:util";
|
|
5859
|
+
import { Context as Context7, Effect as Effect12, Exit as Exit3, Fiber as Fiber2, Layer as Layer6, ManagedRuntime as ManagedRuntime2, Schema as Schema52, Scope as Scope4, Stream as Stream7 } from "effect";
|
|
5860
|
+
import { CancellableLoader } from "@earendil-works/pi-tui";
|
|
5861
|
+
|
|
5862
|
+
// ../../packages/utils/src/process/interactive-process.ts
|
|
5863
|
+
import { spawn } from "node:child_process";
|
|
5864
|
+
import { Deferred as Deferred3, Duration as Duration2, Effect as Effect11, Option as Option17, Schema as Schema51 } from "effect";
|
|
5865
|
+
|
|
5866
|
+
class InteractiveProcessFailed extends Schema51.TaggedError()("InteractiveProcessFailed", {
|
|
5867
|
+
operation: Schema51.Literal("spawn", "run"),
|
|
5868
|
+
message: Schema51.String
|
|
5869
|
+
}) {
|
|
5870
|
+
}
|
|
5871
|
+
var messageFrom = (cause) => cause instanceof Error ? cause.message : String(cause);
|
|
5872
|
+
var definedEnvironment = (environment) => Object.fromEntries(Object.entries(environment).filter((entry) => entry[1] !== undefined));
|
|
5873
|
+
var acquireInteractiveProcess = (input) => Effect11.gen(function* () {
|
|
5874
|
+
const termination = yield* Deferred3.make();
|
|
5875
|
+
const child = yield* Effect11.async((resume) => {
|
|
5876
|
+
let acquired = false;
|
|
5877
|
+
let handle;
|
|
5878
|
+
try {
|
|
5879
|
+
handle = spawn(input.executable, [...input.args], {
|
|
5880
|
+
cwd: input.workingDirectory,
|
|
5881
|
+
detached: false,
|
|
5882
|
+
env: definedEnvironment(input.environment),
|
|
5883
|
+
shell: false,
|
|
5884
|
+
stdio: "inherit"
|
|
5885
|
+
});
|
|
5886
|
+
} catch (cause) {
|
|
5887
|
+
resume(Effect11.fail(new InteractiveProcessFailed({
|
|
5888
|
+
operation: "spawn",
|
|
5889
|
+
message: messageFrom(cause)
|
|
5890
|
+
})));
|
|
5891
|
+
return;
|
|
5892
|
+
}
|
|
5893
|
+
const onError = (cause) => {
|
|
5894
|
+
const failure = new InteractiveProcessFailed({
|
|
5895
|
+
operation: acquired ? "run" : "spawn",
|
|
5896
|
+
message: messageFrom(cause)
|
|
5897
|
+
});
|
|
5898
|
+
if (acquired) {
|
|
5899
|
+
Deferred3.unsafeDone(termination, Effect11.fail(failure));
|
|
5900
|
+
} else {
|
|
5901
|
+
resume(Effect11.fail(failure));
|
|
5902
|
+
}
|
|
5903
|
+
};
|
|
5904
|
+
const onExit = (code, signal) => {
|
|
5905
|
+
Deferred3.unsafeDone(termination, Effect11.succeed(code === null ? { _tag: "Signaled", signal } : { _tag: "Exited", code }));
|
|
5906
|
+
};
|
|
5907
|
+
const onSpawn = () => {
|
|
5908
|
+
acquired = true;
|
|
5909
|
+
resume(Effect11.succeed(handle));
|
|
5910
|
+
};
|
|
5911
|
+
handle.once("error", onError);
|
|
5912
|
+
handle.once("exit", onExit);
|
|
5913
|
+
handle.once("spawn", onSpawn);
|
|
5914
|
+
return Effect11.sync(() => {
|
|
5915
|
+
if (!acquired) {
|
|
5916
|
+
handle.removeListener("error", onError);
|
|
5917
|
+
handle.removeListener("exit", onExit);
|
|
5918
|
+
handle.removeListener("spawn", onSpawn);
|
|
5919
|
+
if (handle.exitCode === null && handle.signalCode === null) {
|
|
5920
|
+
try {
|
|
5921
|
+
handle.kill("SIGTERM");
|
|
5922
|
+
} catch {}
|
|
5923
|
+
}
|
|
5924
|
+
}
|
|
5925
|
+
});
|
|
5926
|
+
});
|
|
5927
|
+
return { child, termination };
|
|
5928
|
+
});
|
|
5929
|
+
var FORWARDED_SIGNALS = process.platform === "win32" ? ["SIGINT", "SIGTERM", "SIGBREAK"] : ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
5930
|
+
var awaitWithSignalForwarding = ({ child, termination }) => Effect11.acquireUseRelease(Effect11.sync(() => {
|
|
5931
|
+
const listeners = new Map;
|
|
5932
|
+
for (const signal of FORWARDED_SIGNALS) {
|
|
5933
|
+
const listener = () => {
|
|
5934
|
+
try {
|
|
5935
|
+
child.kill(signal);
|
|
5936
|
+
} catch {}
|
|
5937
|
+
};
|
|
5938
|
+
listeners.set(signal, listener);
|
|
5939
|
+
process.on(signal, listener);
|
|
5940
|
+
}
|
|
5941
|
+
return listeners;
|
|
5942
|
+
}), () => Deferred3.await(termination), (listeners) => Effect11.sync(() => {
|
|
5943
|
+
for (const [signal, listener] of listeners) {
|
|
5944
|
+
process.removeListener(signal, listener);
|
|
5945
|
+
}
|
|
5946
|
+
}));
|
|
5947
|
+
var terminateAndReap = ({ child, termination }) => Effect11.gen(function* () {
|
|
5948
|
+
if (yield* Deferred3.isDone(termination))
|
|
5949
|
+
return;
|
|
5950
|
+
yield* Effect11.sync(() => {
|
|
5951
|
+
try {
|
|
5952
|
+
child.kill("SIGTERM");
|
|
5953
|
+
} catch {}
|
|
5954
|
+
});
|
|
5955
|
+
const graceful = yield* Deferred3.await(termination).pipe(Effect11.ignore, Effect11.timeoutOption(Duration2.seconds(2)));
|
|
5956
|
+
if (Option17.isSome(graceful))
|
|
5957
|
+
return;
|
|
5958
|
+
yield* Effect11.sync(() => {
|
|
5959
|
+
try {
|
|
5960
|
+
child.kill("SIGKILL");
|
|
5961
|
+
} catch {}
|
|
5962
|
+
});
|
|
5963
|
+
yield* Deferred3.await(termination).pipe(Effect11.ignore, Effect11.timeout(Duration2.seconds(2)), Effect11.ignore);
|
|
5964
|
+
});
|
|
5965
|
+
var runInteractiveProcess = (input) => Effect11.acquireUseRelease(acquireInteractiveProcess(input), awaitWithSignalForwarding, terminateAndReap);
|
|
5966
|
+
// extensions/setup.ts
|
|
5967
|
+
class PiSetupFailed extends Schema52.TaggedError()("PiSetupFailed", {
|
|
5968
|
+
message: Schema52.String
|
|
5969
|
+
}) {
|
|
5970
|
+
}
|
|
5971
|
+
var failure = (message) => new PiSetupFailed({ message });
|
|
5972
|
+
var prepareMagnitudeCli = (cwd, setMessage = () => Effect12.void) => Effect12.gen(function* () {
|
|
5973
|
+
const fs = yield* FileSystem.FileSystem;
|
|
5974
|
+
const override = process.env.MAGNITUDE_CLI?.trim();
|
|
5975
|
+
const executable = override || "magnitude";
|
|
5976
|
+
const probe = Effect12.scoped(Effect12.gen(function* () {
|
|
5977
|
+
const child = yield* Command2.make(executable, "--version").pipe(Command2.start);
|
|
5978
|
+
const [code, , stderr] = yield* Effect12.all([
|
|
5979
|
+
child.exitCode,
|
|
5980
|
+
Stream7.runDrain(child.stdout),
|
|
5981
|
+
child.stderr.pipe(Stream7.decodeText(), Stream7.runFold("", (text2, part) => (text2 + part).slice(-4096)))
|
|
5982
|
+
], { concurrency: "unbounded" });
|
|
5983
|
+
if (code !== 0)
|
|
5984
|
+
return yield* failure(`Magnitude could not run at ${executable} (exit ${code}). ${stripVTControlCharacters(stderr).trim().slice(-600)}${override ? " Check MAGNITUDE_CLI." : ""}`);
|
|
5985
|
+
}));
|
|
5986
|
+
yield* probe.pipe(Effect12.catchIf((error) => !override && error._tag === "SystemError" && error.reason === "NotFound", (error) => Effect12.gen(function* () {
|
|
5987
|
+
for (const directory of (process.env.PATH ?? "").split(delimiter)) {
|
|
5988
|
+
if (yield* fs.exists(join(directory || cwd, executable)))
|
|
5989
|
+
return yield* error;
|
|
5990
|
+
}
|
|
5991
|
+
yield* setMessage("Installing Magnitude…");
|
|
5992
|
+
yield* Effect12.scoped(Effect12.gen(function* () {
|
|
5993
|
+
const child = yield* Command2.make("npm", "install", "--global", "@magnitudedev/cli").pipe(Command2.workingDirectory(cwd), Command2.start);
|
|
5994
|
+
const [code, , stderr] = yield* Effect12.all([
|
|
5995
|
+
child.exitCode,
|
|
5996
|
+
Stream7.runDrain(child.stdout),
|
|
5997
|
+
child.stderr.pipe(Stream7.decodeText(), Stream7.runFold("", (text2, part) => (text2 + part).slice(-4096)))
|
|
5998
|
+
], { concurrency: "unbounded" });
|
|
5999
|
+
if (code !== 0)
|
|
6000
|
+
return yield* failure(`Magnitude installation failed (exit ${code}). ${stripVTControlCharacters(stderr).trim().slice(-600)} Run /magnitude-setup to retry.`);
|
|
6001
|
+
}));
|
|
6002
|
+
return yield* probe;
|
|
6003
|
+
})), 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" : ""}.`)));
|
|
6004
|
+
return executable;
|
|
6005
|
+
});
|
|
6006
|
+
var withPiPreparation = (ctx, work) => Effect12.acquireUseRelease(Effect12.async((resume, signal) => {
|
|
6007
|
+
let closed;
|
|
6008
|
+
closed = ctx.ui.custom((tui, theme, _keys, done) => {
|
|
6009
|
+
const loader = new CancellableLoader(tui, (text2) => theme.fg("accent", text2), (text2) => theme.fg("muted", text2), "Installing Magnitude…");
|
|
6010
|
+
if (signal.aborted) {
|
|
6011
|
+
loader.dispose();
|
|
6012
|
+
done();
|
|
6013
|
+
} else
|
|
6014
|
+
resume(Effect12.succeed({ loader, finish: () => done(), closed: () => closed }));
|
|
6015
|
+
return loader;
|
|
6016
|
+
});
|
|
6017
|
+
closed.catch((error) => resume(Effect12.fail(failure(`Could not open Pi setup: ${String(error)}`))));
|
|
6018
|
+
}), ({ loader }) => work((message) => Effect12.sync(() => loader.setMessage(message))).pipe(Effect12.raceFirst(Effect12.async((resume) => {
|
|
6019
|
+
loader.onAbort = () => resume(Effect12.fail(failure("Magnitude setup cancelled. Run /magnitude-setup to retry.")));
|
|
6020
|
+
if (loader.signal.aborted)
|
|
6021
|
+
loader.onAbort();
|
|
6022
|
+
return Effect12.sync(() => {
|
|
6023
|
+
loader.onAbort = undefined;
|
|
6024
|
+
});
|
|
6025
|
+
}))), ({ loader, finish, closed }) => Effect12.sync(() => {
|
|
6026
|
+
loader.dispose();
|
|
6027
|
+
finish();
|
|
6028
|
+
}).pipe(Effect12.zipRight(Effect12.promise(closed))));
|
|
6029
|
+
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");
|
|
6030
|
+
var withPiTerminal = (ctx, work) => Effect12.acquireUseRelease(Effect12.async((resume, signal) => {
|
|
6031
|
+
let closed;
|
|
6032
|
+
closed = ctx.ui.custom((tui, _theme, _keys, done) => {
|
|
6033
|
+
if (signal.aborted)
|
|
6034
|
+
done();
|
|
6035
|
+
else
|
|
6036
|
+
resume(Effect12.succeed({ tui, finish: () => done(), closed: () => closed }));
|
|
6037
|
+
return { render: () => [], invalidate: () => {} };
|
|
6038
|
+
});
|
|
6039
|
+
closed.catch((error) => resume(Effect12.fail(failure(`Could not open Pi setup: ${String(error)}`))));
|
|
6040
|
+
}), ({ tui }) => Effect12.sync(() => tui.stop()).pipe(Effect12.zipRight(work)), ({ tui, finish, closed }) => Effect12.sync(() => {
|
|
6041
|
+
try {
|
|
6042
|
+
restoreHostTerminal(tui);
|
|
6043
|
+
tui.start();
|
|
6044
|
+
tui.requestRender(true);
|
|
6045
|
+
} finally {
|
|
6046
|
+
finish();
|
|
6047
|
+
}
|
|
6048
|
+
}).pipe(Effect12.zipRight(Effect12.promise(closed))));
|
|
6049
|
+
var validateSetupTermination = (termination) => {
|
|
6050
|
+
if (termination._tag === "Signaled") {
|
|
6051
|
+
return termination.signal === "SIGINT" ? Effect12.succeed(false) : Effect12.fail(failure(`Magnitude setup stopped unexpectedly (${termination.signal}). Run /magnitude-setup to retry.`));
|
|
6052
|
+
}
|
|
6053
|
+
if (termination.code === 130)
|
|
6054
|
+
return Effect12.succeed(false);
|
|
6055
|
+
return termination.code === 0 ? Effect12.succeed(true) : Effect12.fail(failure(`Magnitude setup failed (exit ${termination.code}). Check the terminal error above; if setup --host pi is unsupported, update @magnitudedev/cli. Run /magnitude-setup to retry.`));
|
|
6056
|
+
};
|
|
6057
|
+
var readSetupModel = Effect12.gen(function* () {
|
|
6058
|
+
const client = yield* MagnitudeClient;
|
|
6059
|
+
const { slots } = yield* client.models.getSlots({}).pipe(Effect12.mapError((error) => failure(`Magnitude setup completed, but the selected model could not be read: ${error._tag === "RpcClientError" ? error.message : formatConnectionError(error)}. Run /reload and select it with /model.`)));
|
|
6060
|
+
if (slots.primary._tag !== "ConfiguredLocal")
|
|
6061
|
+
return yield* failure("Magnitude setup completed, but no local primary model is configured. Run /reload and select a Magnitude model with /model.");
|
|
6062
|
+
return slots.primary.selection.providerModelId;
|
|
6063
|
+
});
|
|
6064
|
+
var PiSetup = Context7.GenericTag("pi/PiSetup");
|
|
6065
|
+
var PiSetupLive = Layer6.effect(PiSetup, Effect12.gen(function* () {
|
|
6066
|
+
const fs = yield* FileSystem.FileSystem;
|
|
6067
|
+
const executor = yield* CommandExecutor3.CommandExecutor;
|
|
6068
|
+
return {
|
|
6069
|
+
run: (ctx) => Effect12.scoped(Effect12.gen(function* () {
|
|
6070
|
+
const executable = yield* withPiPreparation(ctx, (setMessage) => prepareMagnitudeCli(ctx.cwd, setMessage));
|
|
6071
|
+
const termination = yield* withPiTerminal(ctx, runInteractiveProcess({
|
|
6072
|
+
executable,
|
|
6073
|
+
args: ["setup", "--host", "pi"],
|
|
6074
|
+
environment: process.env,
|
|
6075
|
+
workingDirectory: ctx.cwd
|
|
6076
|
+
}));
|
|
6077
|
+
if (!(yield* validateSetupTermination(termination)))
|
|
6078
|
+
return { _tag: "Cancelled" };
|
|
6079
|
+
const modelId = yield* readSetupModel.pipe(Effect12.provide(MagnitudeClient.layer({ autoStart: false }).pipe(Layer6.provide(FetchHttpClient2.layer))));
|
|
6080
|
+
return { _tag: "Completed", modelId };
|
|
6081
|
+
})).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)}`)))
|
|
6082
|
+
};
|
|
6083
|
+
}));
|
|
6084
|
+
var registerMagnitudeSetup = (pi, layer4 = PiSetupLive.pipe(Layer6.provide(NodeContext.layer))) => {
|
|
6085
|
+
const runtime = ManagedRuntime2.make(layer4);
|
|
6086
|
+
const scope = Effect12.runSync(Scope4.make());
|
|
6087
|
+
const gate = Effect12.runSync(Effect12.makeSemaphore(1));
|
|
6088
|
+
const action = (ctx) => Effect12.gen(function* () {
|
|
6089
|
+
if (ctx.mode !== "tui")
|
|
6090
|
+
return yield* failure("Run /magnitude-setup in Pi's interactive terminal.");
|
|
6091
|
+
if (!ctx.isIdle() || ctx.hasPendingMessages())
|
|
6092
|
+
return yield* failure("Wait for the current task to finish, then run /magnitude-setup.");
|
|
6093
|
+
const setup = yield* PiSetup;
|
|
6094
|
+
const result = yield* setup.run(ctx);
|
|
6095
|
+
if (result._tag !== "Completed")
|
|
6096
|
+
return false;
|
|
6097
|
+
yield* Effect12.tryPromise({
|
|
6098
|
+
try: async () => {
|
|
6099
|
+
await ctx.modelRegistry.refresh();
|
|
6100
|
+
const model2 = ctx.modelRegistry.find("magnitude", result.modelId);
|
|
6101
|
+
if (!model2 || !await pi.setModel(model2))
|
|
6102
|
+
throw new Error("The selected model is not available in Pi");
|
|
6103
|
+
},
|
|
6104
|
+
catch: (error) => failure(`Magnitude setup completed, but Pi could not activate the model: ${String(error)}. Run /reload and select it with /model.`)
|
|
6105
|
+
});
|
|
6106
|
+
return true;
|
|
6107
|
+
});
|
|
6108
|
+
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(() => {
|
|
6109
|
+
ctx.ui.notify(error.message, "error");
|
|
6110
|
+
return false;
|
|
6111
|
+
}))), scope).pipe(Effect12.flatMap(Fiber2.join)));
|
|
6112
|
+
pi.registerCommand("magnitude-setup", {
|
|
6113
|
+
description: "Choose and set up a local Magnitude model",
|
|
6114
|
+
handler: async (_args, ctx) => {
|
|
6115
|
+
const reload = await run(ctx);
|
|
6116
|
+
if (reload) {
|
|
6117
|
+
try {
|
|
6118
|
+
await ctx.reload();
|
|
6119
|
+
} catch (error) {
|
|
6120
|
+
throw failure(`Magnitude setup completed and selected the model, but Pi could not reload its resources: ${String(error)}. Run /reload to retry.`);
|
|
6121
|
+
}
|
|
6122
|
+
}
|
|
6123
|
+
}
|
|
6124
|
+
});
|
|
6125
|
+
return {
|
|
6126
|
+
run,
|
|
6127
|
+
dispose: async () => {
|
|
6128
|
+
await Effect12.runPromise(Scope4.close(scope, Exit3.void));
|
|
6129
|
+
await runtime.dispose();
|
|
6130
|
+
}
|
|
6131
|
+
};
|
|
6132
|
+
};
|
|
6133
|
+
|
|
6134
|
+
// extensions/onboarding.ts
|
|
5851
6135
|
var SETUP_QUESTION = "Set up local models with Magnitude now?";
|
|
5852
6136
|
var SETUP_REMINDER = "You can set up local models anytime with `/magnitude-setup`.";
|
|
5853
|
-
var claimSetupOffer = (agentDir) =>
|
|
5854
|
-
const fs = yield*
|
|
6137
|
+
var claimSetupOffer = (agentDir) => Effect13.gen(function* () {
|
|
6138
|
+
const fs = yield* FileSystem3.FileSystem;
|
|
5855
6139
|
const directory = `${agentDir}/magnitude`;
|
|
5856
6140
|
yield* fs.makeDirectory(directory, { recursive: true });
|
|
5857
|
-
return yield* fs.writeFileString(`${directory}/setup-offered`, "", { flag: "wx" }).pipe(
|
|
6141
|
+
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
6142
|
});
|
|
5859
6143
|
function canOfferSetup(ctx, argv) {
|
|
5860
6144
|
if (ctx.mode !== "tui" || !ctx.isIdle() || ctx.hasPendingMessages() || ctx.ui.getEditorText().trim())
|
|
@@ -5866,30 +6150,21 @@ function canOfferSetup(ctx, argv) {
|
|
|
5866
6150
|
const args = parseArgs([...argv]);
|
|
5867
6151
|
return args.messages.length === 0 && args.fileArgs.length === 0;
|
|
5868
6152
|
}
|
|
5869
|
-
var offerSetup = (pi, ctx, agentDir, signal) =>
|
|
6153
|
+
var offerSetup = (pi, ctx, agentDir, signal, runSetup) => Effect13.gen(function* () {
|
|
5870
6154
|
if (!(yield* claimSetupOffer(agentDir)) || signal.aborted)
|
|
5871
6155
|
return;
|
|
5872
|
-
const accepted = yield*
|
|
6156
|
+
const accepted = yield* Effect13.tryPromise(() => ctx.ui.confirm(SETUP_QUESTION, "", { signal }));
|
|
5873
6157
|
if (signal.aborted)
|
|
5874
6158
|
return;
|
|
5875
|
-
if (accepted && ctx.isIdle() && !ctx.hasPendingMessages())
|
|
5876
|
-
|
|
5877
|
-
else
|
|
6159
|
+
if (accepted && ctx.isIdle() && !ctx.hasPendingMessages()) {
|
|
6160
|
+
yield* Effect13.tryPromise(() => runSetup(ctx));
|
|
6161
|
+
} else
|
|
5878
6162
|
pi.sendMessage({ customType: "magnitude-setup", content: SETUP_REMINDER, display: true });
|
|
5879
6163
|
});
|
|
5880
6164
|
function registerMagnitudeOnboarding(pi) {
|
|
5881
6165
|
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
|
-
});
|
|
6166
|
+
const scope = Effect13.runSync(Scope5.make());
|
|
6167
|
+
const setup = registerMagnitudeSetup(pi);
|
|
5893
6168
|
pi.on("resources_discover", () => {
|
|
5894
6169
|
if (parseArgs(process.argv.slice(2)).noSkills || pi.getCommands().some((command) => command.source === "skill" && command.name === "skill:magnitude"))
|
|
5895
6170
|
return;
|
|
@@ -5898,24 +6173,25 @@ function registerMagnitudeOnboarding(pi) {
|
|
|
5898
6173
|
pi.on("session_start", (event, ctx) => {
|
|
5899
6174
|
if (event.reason !== "startup" || !canOfferSetup(ctx, process.argv.slice(2)))
|
|
5900
6175
|
return;
|
|
5901
|
-
|
|
6176
|
+
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
6177
|
});
|
|
5903
|
-
return () => {
|
|
6178
|
+
return async () => {
|
|
5904
6179
|
abort.abort();
|
|
5905
|
-
|
|
6180
|
+
await Effect13.runPromise(Scope5.close(scope, Exit4.void));
|
|
6181
|
+
await setup.dispose();
|
|
5906
6182
|
};
|
|
5907
6183
|
}
|
|
5908
6184
|
|
|
5909
6185
|
// extensions/observing-fetch.ts
|
|
5910
|
-
import { Effect as
|
|
6186
|
+
import { Effect as Effect14, Either as Either3, Schema as Schema54 } from "effect";
|
|
5911
6187
|
|
|
5912
6188
|
// extensions/protocol.ts
|
|
5913
|
-
import { Either as Either2, Schema as
|
|
5914
|
-
var ProgressChunkSchema =
|
|
5915
|
-
var TimingsChunkSchema =
|
|
6189
|
+
import { Either as Either2, Schema as Schema53 } from "effect";
|
|
6190
|
+
var ProgressChunkSchema = Schema53.Struct({ progress: MagnitudeProgressSchema });
|
|
6191
|
+
var TimingsChunkSchema = Schema53.Struct({ timings: MagnitudeTimingsSchema });
|
|
5916
6192
|
var decodeMagnitudeObservation = (value) => {
|
|
5917
|
-
const progress =
|
|
5918
|
-
const timings =
|
|
6193
|
+
const progress = Schema53.decodeUnknownEither(ProgressChunkSchema)(value);
|
|
6194
|
+
const timings = Schema53.decodeUnknownEither(TimingsChunkSchema)(value);
|
|
5919
6195
|
if (Either2.isLeft(progress) && Either2.isLeft(timings))
|
|
5920
6196
|
return;
|
|
5921
6197
|
return {
|
|
@@ -5926,7 +6202,7 @@ var decodeMagnitudeObservation = (value) => {
|
|
|
5926
6202
|
|
|
5927
6203
|
// extensions/observing-fetch.ts
|
|
5928
6204
|
var MAGNITUDE_PROGRESS_HEADER = "Magnitude-Include-Progress";
|
|
5929
|
-
var JsonDocumentSchema =
|
|
6205
|
+
var JsonDocumentSchema = Schema54.parseJson(Schema54.Unknown);
|
|
5930
6206
|
|
|
5931
6207
|
class SseDataParser {
|
|
5932
6208
|
#buffer = "";
|
|
@@ -5973,26 +6249,26 @@ class SseDataParser {
|
|
|
5973
6249
|
this.#dataLines = [];
|
|
5974
6250
|
if (data === "[DONE]")
|
|
5975
6251
|
return;
|
|
5976
|
-
const decoded =
|
|
6252
|
+
const decoded = Schema54.decodeUnknownEither(JsonDocumentSchema)(data);
|
|
5977
6253
|
if (Either3.isRight(decoded))
|
|
5978
6254
|
observe(decoded.right);
|
|
5979
6255
|
}
|
|
5980
6256
|
}
|
|
5981
|
-
var observeResponse = (response, request, signal) =>
|
|
6257
|
+
var observeResponse = (response, request, signal) => Effect14.scoped(Effect14.gen(function* () {
|
|
5982
6258
|
if (!response.ok || response.body === null) {
|
|
5983
6259
|
yield* request.fail;
|
|
5984
6260
|
return;
|
|
5985
6261
|
}
|
|
5986
|
-
const reader = yield*
|
|
6262
|
+
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
6263
|
if (signal.aborted) {
|
|
5988
6264
|
yield* request.fail;
|
|
5989
6265
|
return;
|
|
5990
6266
|
}
|
|
5991
6267
|
const decoder = new TextDecoder;
|
|
5992
6268
|
const parser = new SseDataParser;
|
|
5993
|
-
const drain =
|
|
6269
|
+
const drain = Effect14.gen(function* () {
|
|
5994
6270
|
while (true) {
|
|
5995
|
-
const result = yield*
|
|
6271
|
+
const result = yield* Effect14.tryPromise(() => reader.read());
|
|
5996
6272
|
if (result.done)
|
|
5997
6273
|
break;
|
|
5998
6274
|
const values = [];
|
|
@@ -6005,35 +6281,35 @@ var observeResponse = (response, request, signal) => Effect12.scoped(Effect12.ge
|
|
|
6005
6281
|
}
|
|
6006
6282
|
yield* request.finish;
|
|
6007
6283
|
});
|
|
6008
|
-
const aborted =
|
|
6009
|
-
const abort = () => resume(
|
|
6284
|
+
const aborted = Effect14.async((resume) => {
|
|
6285
|
+
const abort = () => resume(Effect14.interrupt);
|
|
6010
6286
|
signal.addEventListener("abort", abort, { once: true });
|
|
6011
6287
|
if (signal.aborted)
|
|
6012
6288
|
abort();
|
|
6013
|
-
return
|
|
6289
|
+
return Effect14.sync(() => signal.removeEventListener("abort", abort));
|
|
6014
6290
|
});
|
|
6015
|
-
yield*
|
|
6016
|
-
})).pipe(
|
|
6291
|
+
yield* Effect14.raceFirst(drain, aborted).pipe(Effect14.onError(() => request.fail));
|
|
6292
|
+
})).pipe(Effect14.catchAllCause(() => Effect14.void));
|
|
6017
6293
|
var makeObservingFetch = (fetchImplementation, begin, scope) => {
|
|
6018
6294
|
const observingFetch = async (input, init) => {
|
|
6019
6295
|
const request = new Request(input, init);
|
|
6020
6296
|
request.headers.set(MAGNITUDE_PROGRESS_HEADER, "true");
|
|
6021
|
-
const progress =
|
|
6022
|
-
observe: () =>
|
|
6023
|
-
finish:
|
|
6024
|
-
fail:
|
|
6297
|
+
const progress = Effect14.runSync(Effect14.suspend(begin).pipe(Effect14.catchAllCause(() => Effect14.succeed({
|
|
6298
|
+
observe: () => Effect14.void,
|
|
6299
|
+
finish: Effect14.void,
|
|
6300
|
+
fail: Effect14.void
|
|
6025
6301
|
}))));
|
|
6026
6302
|
try {
|
|
6027
6303
|
const response = await fetchImplementation(request);
|
|
6028
6304
|
try {
|
|
6029
6305
|
const observation = observeResponse(response.clone(), progress, request.signal);
|
|
6030
|
-
|
|
6306
|
+
Effect14.runFork(Effect14.forkIn(observation, scope));
|
|
6031
6307
|
} catch {
|
|
6032
|
-
|
|
6308
|
+
Effect14.runSync(progress.fail.pipe(Effect14.catchAllCause(() => Effect14.void)));
|
|
6033
6309
|
}
|
|
6034
6310
|
return response;
|
|
6035
6311
|
} catch (error) {
|
|
6036
|
-
|
|
6312
|
+
Effect14.runSync(progress.fail.pipe(Effect14.catchAllCause(() => Effect14.void)));
|
|
6037
6313
|
throw error;
|
|
6038
6314
|
}
|
|
6039
6315
|
};
|
|
@@ -6044,9 +6320,9 @@ var makeObservingFetch = (fetchImplementation, begin, scope) => {
|
|
|
6044
6320
|
|
|
6045
6321
|
// extensions/progress.ts
|
|
6046
6322
|
import { Text } from "@earendil-works/pi-tui";
|
|
6047
|
-
import { Data as Data3, Effect as
|
|
6323
|
+
import { Data as Data3, Effect as Effect15, Fiber as Fiber3, Option as Option18, Queue, Schema as Schema55 } from "effect";
|
|
6048
6324
|
var MAGNITUDE_SUMMARY_WIDGET_KEY = "magnitude-inference-summary";
|
|
6049
|
-
var RequestId =
|
|
6325
|
+
var RequestId = Schema55.Number.pipe(Schema55.int(), Schema55.brand("ProgressRequestId"));
|
|
6050
6326
|
|
|
6051
6327
|
class Observing extends Data3.TaggedClass("Observing") {
|
|
6052
6328
|
}
|
|
@@ -6101,31 +6377,31 @@ var formatLiveProgress = ({ progress, modelName, startedAt }, now) => {
|
|
|
6101
6377
|
}
|
|
6102
6378
|
}
|
|
6103
6379
|
};
|
|
6104
|
-
var makeProgressTracker = (ui, now = () => performance.now()) =>
|
|
6380
|
+
var makeProgressTracker = (ui, now = () => performance.now()) => Effect15.gen(function* () {
|
|
6105
6381
|
let state = new Idle2;
|
|
6106
6382
|
let nextId = 0;
|
|
6107
6383
|
let active;
|
|
6108
6384
|
let latest;
|
|
6109
6385
|
let nextResponseId = 0;
|
|
6110
6386
|
const wake = yield* Queue.sliding(1);
|
|
6111
|
-
const present = (f) =>
|
|
6387
|
+
const present = (f) => Effect15.sync(f).pipe(Effect15.catchAllCause(() => Effect15.void));
|
|
6112
6388
|
const clearSummary = present(() => ui.setWidget(MAGNITUDE_SUMMARY_WIDGET_KEY, undefined));
|
|
6113
6389
|
const resetRow = present(() => ui.setWorkingMessage());
|
|
6114
6390
|
const render2 = present(() => {
|
|
6115
6391
|
if (active)
|
|
6116
6392
|
ui.setWorkingMessage(formatLiveProgress(active.phase, now()));
|
|
6117
6393
|
});
|
|
6118
|
-
const finalize =
|
|
6394
|
+
const finalize = Effect15.gen(function* () {
|
|
6119
6395
|
if (state._tag !== "Settled")
|
|
6120
6396
|
return;
|
|
6121
|
-
if ([...state.run.responses.values()].some(
|
|
6397
|
+
if ([...state.run.responses.values()].some(Option18.isNone))
|
|
6122
6398
|
return;
|
|
6123
6399
|
if ([...state.run.requests.values()].some((r) => r._tag === "Observing"))
|
|
6124
6400
|
return;
|
|
6125
6401
|
const settled = state;
|
|
6126
6402
|
state = runMachine.transition(settled, "Idle", {});
|
|
6127
6403
|
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) => !
|
|
6404
|
+
if ([...settled.run.responses.values()].some((outcome) => !Option18.getOrElse(outcome, () => false)) || timings.length === 0) {
|
|
6129
6405
|
yield* clearSummary;
|
|
6130
6406
|
return;
|
|
6131
6407
|
}
|
|
@@ -6134,7 +6410,7 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect13.gen(fu
|
|
|
6134
6410
|
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
6411
|
yield* present(() => ui.setWidget(MAGNITUDE_SUMMARY_WIDGET_KEY, (_tui, theme) => new Text(theme.fg("muted", summary), 0, 0)));
|
|
6136
6412
|
});
|
|
6137
|
-
const clear =
|
|
6413
|
+
const clear = Effect15.gen(function* () {
|
|
6138
6414
|
if (state._tag === "Disposed")
|
|
6139
6415
|
return;
|
|
6140
6416
|
if (state._tag !== "Idle")
|
|
@@ -6144,7 +6420,7 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect13.gen(fu
|
|
|
6144
6420
|
yield* resetRow;
|
|
6145
6421
|
yield* clearSummary;
|
|
6146
6422
|
});
|
|
6147
|
-
const startRun = (modelName) =>
|
|
6423
|
+
const startRun = (modelName) => Effect15.gen(function* () {
|
|
6148
6424
|
if (state._tag === "Disposed")
|
|
6149
6425
|
return;
|
|
6150
6426
|
if (state._tag === "Settled")
|
|
@@ -6154,38 +6430,38 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect13.gen(fu
|
|
|
6154
6430
|
state = runMachine.transition(state, "Working", { run: { startedAt: now(), modelName, requests: new Map, completed: new Map, responses: new Map, accepted: new Set } });
|
|
6155
6431
|
yield* clearSummary;
|
|
6156
6432
|
});
|
|
6157
|
-
const timer = yield*
|
|
6433
|
+
const timer = yield* Effect15.forever(Effect15.gen(function* () {
|
|
6158
6434
|
yield* Queue.take(wake);
|
|
6159
6435
|
while (active) {
|
|
6160
|
-
yield*
|
|
6436
|
+
yield* Effect15.sleep("100 millis");
|
|
6161
6437
|
yield* render2;
|
|
6162
6438
|
}
|
|
6163
|
-
})).pipe(
|
|
6164
|
-
yield*
|
|
6439
|
+
})).pipe(Effect15.forkScoped);
|
|
6440
|
+
yield* Effect15.addFinalizer(() => Effect15.gen(function* () {
|
|
6165
6441
|
yield* clear;
|
|
6166
6442
|
if (state._tag !== "Disposed")
|
|
6167
6443
|
state = runMachine.transition(state, "Disposed", {});
|
|
6168
|
-
yield*
|
|
6444
|
+
yield* Fiber3.interrupt(timer);
|
|
6169
6445
|
}));
|
|
6170
6446
|
return {
|
|
6171
6447
|
startRun,
|
|
6172
6448
|
clear,
|
|
6173
|
-
settleRun:
|
|
6449
|
+
settleRun: Effect15.gen(function* () {
|
|
6174
6450
|
if (state._tag === "Working")
|
|
6175
6451
|
state = runMachine.transition(state, "Settled", { settledAt: now() });
|
|
6176
6452
|
yield* finalize;
|
|
6177
6453
|
}),
|
|
6178
|
-
beginResponse: (modelName) =>
|
|
6454
|
+
beginResponse: (modelName) => Effect15.gen(function* () {
|
|
6179
6455
|
yield* startRun(modelName);
|
|
6180
6456
|
const run = state._tag === "Working" ? state.run : undefined;
|
|
6181
6457
|
const responseId = ++nextResponseId;
|
|
6182
6458
|
const requests = [];
|
|
6183
|
-
run?.responses.set(responseId,
|
|
6459
|
+
run?.responses.set(responseId, Option18.none());
|
|
6184
6460
|
const belongs = () => run !== undefined && (state._tag === "Working" || state._tag === "Settled") && state.run === run;
|
|
6185
|
-
const end = (successful) =>
|
|
6186
|
-
if (!belongs() ||
|
|
6461
|
+
const end = (successful) => Effect15.gen(function* () {
|
|
6462
|
+
if (!belongs() || Option18.isSome(run.responses.get(responseId)))
|
|
6187
6463
|
return;
|
|
6188
|
-
run.responses.set(responseId,
|
|
6464
|
+
run.responses.set(responseId, Option18.some(successful));
|
|
6189
6465
|
const last = requests.at(-1);
|
|
6190
6466
|
if (successful && last !== undefined)
|
|
6191
6467
|
run.accepted.add(last);
|
|
@@ -6204,16 +6480,16 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect13.gen(fu
|
|
|
6204
6480
|
}
|
|
6205
6481
|
yield* finalize;
|
|
6206
6482
|
});
|
|
6207
|
-
const begin =
|
|
6208
|
-
if (!belongs() ||
|
|
6209
|
-
return { observe: () =>
|
|
6483
|
+
const begin = Effect15.gen(function* () {
|
|
6484
|
+
if (!belongs() || Option18.isSome(run.responses.get(responseId)))
|
|
6485
|
+
return { observe: () => Effect15.void, finish: Effect15.void, fail: Effect15.void };
|
|
6210
6486
|
const id = RequestId.make(++nextId);
|
|
6211
6487
|
requests.push(id);
|
|
6212
6488
|
latest = id;
|
|
6213
6489
|
active = undefined;
|
|
6214
|
-
run?.requests.set(id, new Observing({ timings:
|
|
6490
|
+
run?.requests.set(id, new Observing({ timings: Option18.none() }));
|
|
6215
6491
|
yield* resetRow;
|
|
6216
|
-
const close = (failed) =>
|
|
6492
|
+
const close = (failed) => Effect15.gen(function* () {
|
|
6217
6493
|
if (!belongs())
|
|
6218
6494
|
return;
|
|
6219
6495
|
const request = run.requests.get(id);
|
|
@@ -6223,7 +6499,7 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect13.gen(fu
|
|
|
6223
6499
|
run.requests.set(id, requestMachine.transition(request, "Closed", {}));
|
|
6224
6500
|
else {
|
|
6225
6501
|
run.requests.set(id, requestMachine.transition(request, "Observed", {}));
|
|
6226
|
-
if (
|
|
6502
|
+
if (Option18.isSome(request.timings))
|
|
6227
6503
|
run.completed.set(id, request.timings.value);
|
|
6228
6504
|
}
|
|
6229
6505
|
if (latest === id) {
|
|
@@ -6233,14 +6509,14 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect13.gen(fu
|
|
|
6233
6509
|
yield* finalize;
|
|
6234
6510
|
});
|
|
6235
6511
|
return {
|
|
6236
|
-
observe: (observation) =>
|
|
6512
|
+
observe: (observation) => Effect15.gen(function* () {
|
|
6237
6513
|
if (!belongs())
|
|
6238
6514
|
return;
|
|
6239
6515
|
const request = run.requests.get(id);
|
|
6240
6516
|
if (request._tag !== "Observing")
|
|
6241
6517
|
return;
|
|
6242
6518
|
if (observation.timings)
|
|
6243
|
-
run.requests.set(id, requestMachine.hold(request, { timings:
|
|
6519
|
+
run.requests.set(id, requestMachine.hold(request, { timings: Option18.some(observation.timings) }));
|
|
6244
6520
|
if (latest !== id || !observation.progress)
|
|
6245
6521
|
return;
|
|
6246
6522
|
const progress = observation.progress;
|
|
@@ -6264,35 +6540,35 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect13.gen(fu
|
|
|
6264
6540
|
});
|
|
6265
6541
|
|
|
6266
6542
|
// extensions/magnitude.ts
|
|
6267
|
-
import { Effect as
|
|
6543
|
+
import { Effect as Effect16, Exit as Exit5, Scope as Scope7 } from "effect";
|
|
6268
6544
|
function magnitudeExtension(pi) {
|
|
6269
6545
|
const completions = openAICompletionsApi();
|
|
6270
6546
|
let tracker;
|
|
6271
6547
|
let scope;
|
|
6272
6548
|
const disposeCommands = registerMagnitudeCommands(pi);
|
|
6273
6549
|
const disposeOnboarding = registerMagnitudeOnboarding(pi);
|
|
6274
|
-
const perform = (effect) =>
|
|
6550
|
+
const perform = (effect) => Effect16.runSync((effect ?? Effect16.void).pipe(Effect16.catchAllCause(() => Effect16.void)));
|
|
6275
6551
|
pi.registerProvider("magnitude", {
|
|
6276
6552
|
api: "openai-completions",
|
|
6277
6553
|
streamSimple: (model2, context, options) => {
|
|
6278
6554
|
if (model2.api !== "openai-completions") {
|
|
6279
6555
|
throw new Error(`Magnitude for Pi requires openai-completions, received ${model2.api}`);
|
|
6280
6556
|
}
|
|
6281
|
-
const response = tracker &&
|
|
6557
|
+
const response = tracker && Effect16.runSync(tracker.beginResponse(model2.name));
|
|
6282
6558
|
const stream = completions.streamSimple(model2, context, {
|
|
6283
6559
|
...options,
|
|
6284
|
-
fetch: scope ? makeObservingFetch(options?.fetch ?? globalThis.fetch, () => response?.begin ??
|
|
6560
|
+
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
6561
|
});
|
|
6286
6562
|
if (response && scope)
|
|
6287
|
-
|
|
6563
|
+
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
6564
|
return stream;
|
|
6289
6565
|
}
|
|
6290
6566
|
});
|
|
6291
6567
|
pi.on("session_start", async (_event, ctx) => {
|
|
6292
6568
|
if (scope)
|
|
6293
|
-
await
|
|
6294
|
-
scope =
|
|
6295
|
-
tracker = await
|
|
6569
|
+
await Effect16.runPromise(Scope7.close(scope, Exit5.void));
|
|
6570
|
+
scope = Effect16.runSync(Scope7.make());
|
|
6571
|
+
tracker = await Effect16.runPromise(makeProgressTracker(ctx.ui).pipe(Scope7.extend(scope)));
|
|
6296
6572
|
});
|
|
6297
6573
|
pi.on("model_select", (event) => {
|
|
6298
6574
|
if (event.model.provider !== "magnitude")
|
|
@@ -6306,7 +6582,7 @@ function magnitudeExtension(pi) {
|
|
|
6306
6582
|
pi.on("session_shutdown", async () => {
|
|
6307
6583
|
await disposeOnboarding();
|
|
6308
6584
|
if (scope)
|
|
6309
|
-
await
|
|
6585
|
+
await Effect16.runPromise(Scope7.close(scope, Exit5.void));
|
|
6310
6586
|
tracker = undefined;
|
|
6311
6587
|
scope = undefined;
|
|
6312
6588
|
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.3",
|
|
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:*",
|