@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 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, FileSystem, Layer, Logger, Option, Predicate, Redacted, Schema, Stdio, Stream } from "effect";
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.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);
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.choice("output", [
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.boolean("quiet").pipe(Flag.withAlias("q"), Flag.withDescription("Suppress command output"));
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.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.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()}`))
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.text({
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.select({
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.integer({
662
+ integer: (message, options) => Prompt.run(Prompt.Int({
663
663
  message,
664
664
  ...options
665
665
  })),
666
- confirm: (message, initial = false) => Prompt.run(Prompt.confirm({
666
+ confirm: (message, initial = false) => Prompt.run(Prompt.Confirm({
667
667
  message,
668
668
  initial
669
669
  })),
670
- signatureType: Prompt.run(Prompt.select({
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.text({ message })),
683
- typedData: Prompt.run(Prompt.text({
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.date({ message }))
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.string("max-gas-cost-wei").pipe(Flag.withDescription("Local fee ceiling for self-funded operations, in wei"), Flag.optional)
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.string("submission-id"),
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.string("cursor").pipe(Flag.optional),
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, FileSystem.KiB(32)), Effect.timeout("15 seconds"), Effect.catchTag("TimeoutError", () => Effect.fail(new LocalOAuthError({ code: "temporarily_unavailable" }))), Effect.scoped);
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.string("profile").pipe(Flag.withDefault("default")),
1375
- host: Flag.string("host").pipe(Flag.withDefault(NAMERA_API_ORIGIN), Flag.withDescription("Namera API origin"))
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.string("max-gas-cost-wei").pipe(Flag.optional, Flag.withDescription("Fee ceiling for self-funded operations; default is sponsored only"))
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.string("encrypted-export"),
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.password({ message: "Export passphrase" });
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.string("wallet").pipe(Flag.withDescription("Only list keys for one wallet"), Flag.optional),
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.string("session-key-id"),
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.string("wallet-id"),
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);