totemlayer 0.1.3 → 0.1.5

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.
@@ -0,0 +1,36 @@
1
+ import { TotemError } from "./errors.js";
2
+ export function requireActionChecksKey(apiKey) {
3
+ if (typeof apiKey !== "string" || !/^at_(?:test|live)_[A-Za-z0-9_-]{43}$/.test(apiKey)) {
4
+ throw new TotemError("A separate scoped Action Checks API key (at_test_ or at_live_) is required; Network credentials cannot authorize actions.", { code: "INVALID_CONFIG" });
5
+ }
6
+ return apiKey;
7
+ }
8
+ export function assistantCheckOptions(options) {
9
+ const { actionChecksApiKey, ...requestOptions } = options ?? {};
10
+ return {
11
+ ...signalOnlyOptions(requestOptions),
12
+ headers: { "X-Totem-Action-Key": requireActionChecksKey(actionChecksApiKey) },
13
+ };
14
+ }
15
+ export function assertInputFields(input, allowed) {
16
+ if (!input ||
17
+ typeof input !== "object" ||
18
+ Array.isArray(input) ||
19
+ Object.keys(input).some((key) => !allowed.includes(key))) {
20
+ throw new TotemError("The request contains unsupported fields; caller identity comes from credentials.", {
21
+ code: "INVALID_INPUT",
22
+ });
23
+ }
24
+ }
25
+ /** These APIs derive caller identity from credentials, never from caller options. */
26
+ export function signalOnlyOptions(options = {}) {
27
+ if (!options ||
28
+ typeof options !== "object" ||
29
+ Array.isArray(options) ||
30
+ Object.keys(options).some((key) => key !== "signal")) {
31
+ throw new TotemError("Only an abort signal may be supplied as request options.", {
32
+ code: "INVALID_REQUEST_OPTIONS",
33
+ });
34
+ }
35
+ return { signal: options.signal };
36
+ }
@@ -25,7 +25,8 @@ export class Transport {
25
25
  #timeoutMs;
26
26
  #maxRetries;
27
27
  #retryDelayMs;
28
- constructor(options) {
28
+ #responseEnvelope;
29
+ constructor(options, api = undefined) {
29
30
  if (!options.apiKey?.trim())
30
31
  throw new TotemError("An agent API key is required.", { code: "INVALID_CONFIG" });
31
32
  const url = new URL(options.baseUrl ?? "https://totemlayer.com");
@@ -41,7 +42,8 @@ export class Transport {
41
42
  });
42
43
  }
43
44
  this.#apiKey = options.apiKey;
44
- this.#baseUrl = `${url.origin}/api/v1/network`;
45
+ this.#baseUrl = `${url.origin}${api?.basePath ?? "/api/v1/network"}`;
46
+ this.#responseEnvelope = api?.responseEnvelope ?? "data";
45
47
  this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
46
48
  this.#timeoutMs = options.timeoutMs ?? 15_000;
47
49
  this.#maxRetries = options.maxRetries ?? 2;
@@ -117,7 +119,10 @@ export class Transport {
117
119
  }
118
120
  if (!result.ok)
119
121
  throw apiError(result.status, payload, requestId);
120
- if (!payload || typeof payload !== "object" || !("data" in payload)) {
122
+ if (!payload ||
123
+ typeof payload !== "object" ||
124
+ Array.isArray(payload) ||
125
+ (this.#responseEnvelope === "data" && !("data" in payload))) {
121
126
  throw new TotemError("Totem returned an invalid response envelope.", {
122
127
  code: "INVALID_RESPONSE",
123
128
  requestId,
package/bin/totem.mjs CHANGED
@@ -4,7 +4,7 @@ import { createHash, randomUUID } from "node:crypto";
4
4
  import { createReadStream } from "node:fs";
5
5
  import { stripVTControlCharacters } from "node:util";
6
6
 
7
- const CLI_VERSION = "0.1.3";
7
+ const CLI_VERSION = "0.1.5";
8
8
  const DEFAULT_BASE_URL = "https://totemlayer.com";
9
9
  const API_KEY_PATTERN = /^at_(?:live|test)_[A-Za-z0-9_-]{20,}$/;
10
10
  const ACTION_PATTERN = /^[a-z0-9][a-z0-9._:-]*$/;
@@ -241,9 +241,16 @@ function help() {
241
241
  Confirm installation totem --version
242
242
  Run one-off npx --yes totemlayer --help
243
243
 
244
+ Connect once and receive Mission work (local preview)
245
+ Requires the matching local package; not yet published to npm.
246
+ totem setup
247
+ totem status
248
+ totem start --background
249
+ totem stop
250
+
244
251
  Agent messaging and local runtime
245
252
  totem network --help
246
- totem network run --provider ollama --model qwen3.5:9b
253
+ totem network run --provider qwen-local --model qwen3.5-9b
247
254
 
248
255
  Connect an AI app through MCP
249
256
  totem mcp
@@ -1741,6 +1748,20 @@ async function runRfq(options, baseUrl) {
1741
1748
  }
1742
1749
 
1743
1750
  async function main() {
1751
+ if (["setup", "status", "doctor"].includes(process.argv[2])) {
1752
+ const { agentSetupMain, agentStatusMain } = await import("./agent-setup.mjs");
1753
+ const { readAgentRuntimeStatus } = await import("./agent-runner.mjs");
1754
+ const command = process.argv[2] === "setup" ? agentSetupMain : agentStatusMain;
1755
+ process.exitCode = await command(process.argv.slice(3), {
1756
+ readRuntimeStatus: readAgentRuntimeStatus,
1757
+ });
1758
+ return;
1759
+ }
1760
+ if (["start", "stop"].includes(process.argv[2])) {
1761
+ const { agentRunnerMain } = await import("./agent-runner.mjs");
1762
+ process.exitCode = await agentRunnerMain(process.argv[2], process.argv.slice(3));
1763
+ return;
1764
+ }
1744
1765
  if (process.argv[2] === "mcp") {
1745
1766
  if (process.argv[3] === "install" || process.argv[3] === "--help" || process.argv[3] === "-h") {
1746
1767
  const { installTotemMcp } = await import("./mcp-install.mjs");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "totemlayer",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Cross-platform CLI for TotemLayer agent identity and authorization",
5
5
  "type": "module",
6
6
  "bin": {