@namera-ai/cli 1.0.0 → 1.0.1
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 +4 -0
- package/dist/index.js +28 -28
- package/dist/index.js.map +1 -1
- package/npm-shrinkwrap.json +666 -0
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -135,3 +135,7 @@ or in-memory fallback.
|
|
|
135
135
|
See [local MCP](https://github.com/thenamespace/namera-core/blob/main/architecture/clients/local-mcp.md)
|
|
136
136
|
and [key storage](https://github.com/thenamespace/namera-core/blob/main/architecture/clients/local-keystore.md).
|
|
137
137
|
The four public packages share a release version starting with 1.0.0.
|
|
138
|
+
|
|
139
|
+
The CLI includes an npm shrinkwrap to keep its tested runtime dependencies together.
|
|
140
|
+
Update with `npm install -g @namera-ai/cli@latest`; no separate Effect installation
|
|
141
|
+
is needed. This does not change your saved profiles or local keys.
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { NodeRuntime, NodeServices } from "@effect/platform-node";
|
|
3
|
-
import { Clock, Console, Context, DateTime, Deferred, Effect, Fiber,
|
|
3
|
+
import { ByteSize, Clock, Console, Context, DateTime, Deferred, Effect, Fiber, Layer, Logger, Option, Predicate, Redacted, Schema, Stdio, Stream } from "effect";
|
|
4
4
|
import { Argument, Command, Flag, Prompt } from "effect/unstable/cli";
|
|
5
5
|
import { NAMERA_API_ORIGIN, NameraClient, openLocalSessionKey, sealLocalSessionKey } from "@namera-ai/sdk";
|
|
6
6
|
import { chmod, link, lstat, mkdir, open, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
@@ -22,8 +22,8 @@ import { createServer } from "node:http";
|
|
|
22
22
|
import { McpProtocol, McpSchema, McpServer, Tool } from "effect/unstable/ai";
|
|
23
23
|
|
|
24
24
|
//#region src/commands/common.ts
|
|
25
|
-
const profileFlag = Flag.
|
|
26
|
-
const paramsFlag = Flag.
|
|
25
|
+
const profileFlag = Flag.String("profile").pipe(Flag.withDescription("CLI profile name"), Flag.withDefault("personal"));
|
|
26
|
+
const paramsFlag = Flag.String("params").pipe(Flag.withDescription("Inline JSON command payload"), Flag.optional);
|
|
27
27
|
const resolveParams = (params, schema, prompt) => Option.match(params, {
|
|
28
28
|
onNone: () => prompt,
|
|
29
29
|
onSome: (value) => Schema.decodeUnknownEffect(Schema.fromJsonString(schema))(value)
|
|
@@ -357,12 +357,12 @@ const makeCliClient = async (profile, maxGasCostWei) => {
|
|
|
357
357
|
|
|
358
358
|
//#endregion
|
|
359
359
|
//#region src/commands/root.ts
|
|
360
|
-
const outputFlag = Flag.
|
|
360
|
+
const outputFlag = Flag.Literals("output", [
|
|
361
361
|
"pretty",
|
|
362
362
|
"json",
|
|
363
363
|
"ndjson"
|
|
364
364
|
]).pipe(Flag.withAlias("o"), Flag.withDescription("Output format"), Flag.withDefault("pretty"));
|
|
365
|
-
const quietFlag = Flag.
|
|
365
|
+
const quietFlag = Flag.Boolean("quiet").pipe(Flag.withAlias("q"), Flag.withDescription("Suppress command output"), Flag.withDefault(false));
|
|
366
366
|
const nameraCommand = Command.make("namera").pipe(Command.withSharedFlags({
|
|
367
367
|
output: outputFlag,
|
|
368
368
|
quiet: quietFlag
|
|
@@ -504,7 +504,7 @@ const authStatusCommand = Command.make("status", { profile: profileFlag }, Effec
|
|
|
504
504
|
|
|
505
505
|
//#endregion
|
|
506
506
|
//#region package.json
|
|
507
|
-
var version$1 = "1.0.
|
|
507
|
+
var version$1 = "1.0.1";
|
|
508
508
|
|
|
509
509
|
//#endregion
|
|
510
510
|
//#region src/version.ts
|
|
@@ -546,8 +546,8 @@ const waitForToken = async (baseUrl, deviceCode, expiresIn, initialInterval) =>
|
|
|
546
546
|
};
|
|
547
547
|
const loginCommand = Command.make("login", {
|
|
548
548
|
profile: profileFlag,
|
|
549
|
-
host: Flag.
|
|
550
|
-
deviceName: Flag.
|
|
549
|
+
host: Flag.String("host").pipe(Flag.withDescription("Namera API origin"), Flag.withDefault(NAMERA_API_ORIGIN)),
|
|
550
|
+
deviceName: Flag.String("device-name").pipe(Flag.withDescription("Friendly name shown on the consent screen"), Flag.withDefault(`Namera CLI on ${platform()}`))
|
|
551
551
|
}, Effect.fn(function* ({ profile, host, deviceName }) {
|
|
552
552
|
const baseUrl = new URL(host).origin;
|
|
553
553
|
const request = yield* Effect.tryPromise(() => startDeviceAuthorization(baseUrl, {
|
|
@@ -639,13 +639,13 @@ const verificationView = (result, colors) => section(result.valid ? "Signature v
|
|
|
639
639
|
|
|
640
640
|
//#endregion
|
|
641
641
|
//#region src/services/prompts.ts
|
|
642
|
-
const schemaText = (message, schema, defaultValue) => Prompt.
|
|
642
|
+
const schemaText = (message, schema, defaultValue) => Prompt.String({
|
|
643
643
|
message,
|
|
644
644
|
...defaultValue === void 0 ? {} : { default: defaultValue },
|
|
645
645
|
validate: (value) => Schema.decodeUnknownEffect(schema)(value).pipe(Effect.as(value), Effect.mapError((error) => error.message))
|
|
646
646
|
});
|
|
647
647
|
const makePrompts = () => ({
|
|
648
|
-
namespace: Prompt.run(Prompt.
|
|
648
|
+
namespace: Prompt.run(Prompt.Select({
|
|
649
649
|
message: "Namespace",
|
|
650
650
|
choices: [{
|
|
651
651
|
title: "EVM",
|
|
@@ -659,15 +659,15 @@ const makePrompts = () => ({
|
|
|
659
659
|
ethereumAddress: (message) => Prompt.run(schemaText(message, EthereumAddress)).pipe(Effect.flatMap(Schema.decodeUnknownEffect(EthereumAddress))),
|
|
660
660
|
ethereumValue: (message = "Native value (wei)") => Prompt.run(schemaText(message, Schema.BigIntFromString.check(Schema.isGreaterThanOrEqualToBigInt(0n)), "0")).pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.BigIntFromString))),
|
|
661
661
|
hex: (message, defaultValue = "0x") => Prompt.run(schemaText(message, Hex, defaultValue)).pipe(Effect.flatMap(Schema.decodeUnknownEffect(Hex))),
|
|
662
|
-
integer: (message, options) => Prompt.run(Prompt.
|
|
662
|
+
integer: (message, options) => Prompt.run(Prompt.Int({
|
|
663
663
|
message,
|
|
664
664
|
...options
|
|
665
665
|
})),
|
|
666
|
-
confirm: (message, initial = false) => Prompt.run(Prompt.
|
|
666
|
+
confirm: (message, initial = false) => Prompt.run(Prompt.Confirm({
|
|
667
667
|
message,
|
|
668
668
|
initial
|
|
669
669
|
})),
|
|
670
|
-
signatureType: Prompt.run(Prompt.
|
|
670
|
+
signatureType: Prompt.run(Prompt.Select({
|
|
671
671
|
message: "Signature type",
|
|
672
672
|
choices: [{
|
|
673
673
|
title: "Message",
|
|
@@ -679,12 +679,12 @@ const makePrompts = () => ({
|
|
|
679
679
|
value: "typed-data"
|
|
680
680
|
}]
|
|
681
681
|
})),
|
|
682
|
-
message: (message = "Message") => Prompt.run(Prompt.
|
|
683
|
-
typedData: Prompt.run(Prompt.
|
|
682
|
+
message: (message = "Message") => Prompt.run(Prompt.String({ message })),
|
|
683
|
+
typedData: Prompt.run(Prompt.String({
|
|
684
684
|
message: "EIP-712 typed data (JSON)",
|
|
685
685
|
validate: (value) => Schema.decodeUnknownEffect(Schema.fromJsonString(EvmTypedData))(value).pipe(Effect.as(value), Effect.mapError((error) => error.message))
|
|
686
686
|
})).pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.fromJsonString(EvmTypedData)))),
|
|
687
|
-
date: (message) => Prompt.run(Prompt.
|
|
687
|
+
date: (message) => Prompt.run(Prompt.Date({ message }))
|
|
688
688
|
});
|
|
689
689
|
var CliPrompts = class CliPrompts extends Context.Service()("@namera-ai/cli/CliPrompts") {
|
|
690
690
|
static layer = Layer.succeed(CliPrompts, CliPrompts.of(makePrompts()));
|
|
@@ -729,7 +729,7 @@ const promptExecutionRequest = Effect.fn("cli.execution.promptExecutionRequest")
|
|
|
729
729
|
const execute = Command.make("execute", {
|
|
730
730
|
params: paramsFlag,
|
|
731
731
|
profile: profileFlag,
|
|
732
|
-
maxGasCost: Flag.
|
|
732
|
+
maxGasCost: Flag.String("max-gas-cost-wei").pipe(Flag.withDescription("Local fee ceiling for self-funded operations, in wei"), Flag.optional)
|
|
733
733
|
}, Effect.fn(function* ({ params, profile, maxGasCost }) {
|
|
734
734
|
const request = yield* resolveParams(params, ExecuteRequest, promptExecutionRequest());
|
|
735
735
|
let maxGasCostWei;
|
|
@@ -747,7 +747,7 @@ const simulate = Command.make("simulate", {
|
|
|
747
747
|
yield* printValue(yield* runPromise(client.executions.simulate(request)), simulationView);
|
|
748
748
|
})).pipe(Command.withDescription("Simulate calls and evaluate session-key policies without signing or submitting"));
|
|
749
749
|
const status$1 = Command.make("status", {
|
|
750
|
-
submissionId: Argument.
|
|
750
|
+
submissionId: Argument.String("submission-id"),
|
|
751
751
|
profile: profileFlag
|
|
752
752
|
}, Effect.fn(function* ({ submissionId, profile }) {
|
|
753
753
|
const id = yield* Schema.decodeUnknownEffect(ExecutionSubmissionId)(submissionId);
|
|
@@ -755,7 +755,7 @@ const status$1 = Command.make("status", {
|
|
|
755
755
|
yield* printValue(yield* runPromise(client.executions.getStatus(id)), executionStatusView);
|
|
756
756
|
}));
|
|
757
757
|
const list$2 = Command.make("list", {
|
|
758
|
-
cursor: Flag.
|
|
758
|
+
cursor: Flag.String("cursor").pipe(Flag.optional),
|
|
759
759
|
profile: profileFlag
|
|
760
760
|
}, Effect.fn(function* ({ cursor, profile }) {
|
|
761
761
|
const decodedCursor = Option.isSome(cursor) ? yield* Schema.decodeUnknownEffect(ExecutionId)(cursor.value) : void 0;
|
|
@@ -979,7 +979,7 @@ const mcpOAuthUpstreamLayer = (config) => Layer.effect(McpOAuthUpstream, Effect.
|
|
|
979
979
|
const response = yield* client.execute(request).pipe(Effect.mapError(() => new LocalOAuthError({ code: "temporarily_unavailable" })));
|
|
980
980
|
if (response.status < 200 || response.status >= 300) return yield* new LocalOAuthError({ code: response.status >= 500 || response.status === 429 ? "temporarily_unavailable" : "invalid_grant" });
|
|
981
981
|
return yield* response.json.pipe(Effect.mapError(() => new LocalOAuthError({ code: "temporarily_unavailable" })));
|
|
982
|
-
}, Effect.provideService(FetchHttpClient.RequestInit, { redirect: "error" }), Effect.provideService(HttpIncomingMessage.MaxBodySize,
|
|
982
|
+
}, Effect.provideService(FetchHttpClient.RequestInit, { redirect: "error" }), Effect.provideService(HttpIncomingMessage.MaxBodySize, ByteSize.kibibytes(32)), Effect.timeout("15 seconds"), Effect.catchTag("TimeoutError", () => Effect.fail(new LocalOAuthError({ code: "temporarily_unavailable" }))), Effect.scoped);
|
|
983
983
|
const tokenRequest = Effect.fn("McpOAuthUpstream.token")(function* (body) {
|
|
984
984
|
const json = yield* requestJson(HttpClientRequest.post(`${config.apiOrigin}/oauth/token`).pipe(HttpClientRequest.bodyUrlParams({
|
|
985
985
|
...body,
|
|
@@ -1371,8 +1371,8 @@ const LocalMcpApiOrigin = Schema.NonEmptyString.check(Schema.makeFilter((value)
|
|
|
1371
1371
|
//#endregion
|
|
1372
1372
|
//#region src/commands/mcp/index.ts
|
|
1373
1373
|
const flags = {
|
|
1374
|
-
profile: Flag.
|
|
1375
|
-
host: Flag.
|
|
1374
|
+
profile: Flag.String("profile").pipe(Flag.withDefault("default")),
|
|
1375
|
+
host: Flag.String("host").pipe(Flag.withDefault(NAMERA_API_ORIGIN), Flag.withDescription("Namera API origin"))
|
|
1376
1376
|
};
|
|
1377
1377
|
const session = Effect.fn("Mcp.session")(function* (options) {
|
|
1378
1378
|
const apiOrigin = new URL(yield* Schema.decodeUnknownEffect(LocalMcpApiOrigin)(options.host)).origin;
|
|
@@ -1385,7 +1385,7 @@ const session = Effect.fn("Mcp.session")(function* (options) {
|
|
|
1385
1385
|
});
|
|
1386
1386
|
const serve = Command.make("serve", {
|
|
1387
1387
|
...flags,
|
|
1388
|
-
maxGasCost: Flag.
|
|
1388
|
+
maxGasCost: Flag.String("max-gas-cost-wei").pipe(Flag.optional, Flag.withDescription("Fee ceiling for self-funded operations; default is sponsored only"))
|
|
1389
1389
|
}, Effect.fn("Mcp.serve")(function* (options) {
|
|
1390
1390
|
const connection = yield* session(options);
|
|
1391
1391
|
const apiOrigin = new URL(options.host).origin;
|
|
@@ -1497,7 +1497,7 @@ const sessionKeysView = (keys, colors) => collection(keys, "session key", "sessi
|
|
|
1497
1497
|
//#endregion
|
|
1498
1498
|
//#region src/commands/session-key/import.ts
|
|
1499
1499
|
const importSessionKeyCommand = Command.make("import", {
|
|
1500
|
-
encryptedExport: Argument.
|
|
1500
|
+
encryptedExport: Argument.String("encrypted-export"),
|
|
1501
1501
|
profile: profileFlag
|
|
1502
1502
|
}, Effect.fn("cli.sessionKey.import")(function* ({ encryptedExport, profile }) {
|
|
1503
1503
|
const envelope = yield* Effect.try({
|
|
@@ -1508,7 +1508,7 @@ const importSessionKeyCommand = Command.make("import", {
|
|
|
1508
1508
|
catch: () => new SessionKeystoreError({ code: "IMPORT_FAILED" })
|
|
1509
1509
|
});
|
|
1510
1510
|
const { profile: activeProfile } = yield* Effect.tryPromise(() => makeCliClient(profile));
|
|
1511
|
-
const password = yield* Prompt.
|
|
1511
|
+
const password = yield* Prompt.Password({ message: "Export passphrase" });
|
|
1512
1512
|
const result = yield* Effect.tryPromise({
|
|
1513
1513
|
try: () => sessionKeystore.importKey(envelope, password, new URL(activeProfile.baseUrl).origin),
|
|
1514
1514
|
catch: () => new SessionKeystoreError({ code: "IMPORT_FAILED" })
|
|
@@ -1522,7 +1522,7 @@ const importSessionKeyCommand = Command.make("import", {
|
|
|
1522
1522
|
//#endregion
|
|
1523
1523
|
//#region src/commands/session-key/index.ts
|
|
1524
1524
|
const list$1 = Command.make("list", {
|
|
1525
|
-
wallet: Flag.
|
|
1525
|
+
wallet: Flag.String("wallet").pipe(Flag.withDescription("Only list keys for one wallet"), Flag.optional),
|
|
1526
1526
|
profile: profileFlag
|
|
1527
1527
|
}, Effect.fn(function* ({ wallet, profile }) {
|
|
1528
1528
|
const { client } = yield* Effect.tryPromise(() => makeCliClient(profile));
|
|
@@ -1530,7 +1530,7 @@ const list$1 = Command.make("list", {
|
|
|
1530
1530
|
yield* printValue(result, sessionKeysView);
|
|
1531
1531
|
}));
|
|
1532
1532
|
const get$1 = Command.make("get", {
|
|
1533
|
-
sessionKeyId: Argument.
|
|
1533
|
+
sessionKeyId: Argument.String("session-key-id"),
|
|
1534
1534
|
profile: profileFlag
|
|
1535
1535
|
}, Effect.fn(function* ({ sessionKeyId, profile }) {
|
|
1536
1536
|
const id = yield* Schema.decodeUnknownEffect(SessionKeyId)(sessionKeyId);
|
|
@@ -1632,7 +1632,7 @@ const list = Command.make("list", { profile: profileFlag }, Effect.fn(function*
|
|
|
1632
1632
|
yield* printValue(yield* runPromise(client.wallets.list()), walletsView);
|
|
1633
1633
|
}));
|
|
1634
1634
|
const get = Command.make("get", {
|
|
1635
|
-
walletId: Argument.
|
|
1635
|
+
walletId: Argument.String("wallet-id"),
|
|
1636
1636
|
profile: profileFlag
|
|
1637
1637
|
}, Effect.fn(function* ({ walletId, profile }) {
|
|
1638
1638
|
const id = yield* Schema.decodeUnknownEffect(WalletId)(walletId);
|