@instafy/cli 0.1.8 → 0.1.10

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
@@ -7,22 +7,29 @@ Run Instafy projects locally and connect them back to Instafy Studio — from an
7
7
  ## Quickstart
8
8
 
9
9
  0. Log in once: `instafy login`
10
+ - Opens a Studio URL; the CLI continues automatically after you sign in.
11
+ - Also enables Git auth (credential helper) for Instafy Git Service. Disable with `instafy login --no-git-setup`.
12
+ - Multiple accounts: `instafy login --profile work` (then bind folders with `instafy project init --profile work` or `instafy project profile work`).
10
13
  - Optional: set defaults with `instafy config set controller-url <url>` / `instafy config set studio-url <url>`
11
14
  1. Link a folder to a project:
12
15
  - VS Code: install the Instafy extension and run `Instafy: Link Workspace to Project`, or
13
- - Terminal: `instafy project:init`
14
- 2. Start the runtime: `instafy runtime:start`
16
+ - Terminal: `instafy project init`
17
+ 2. Start the runtime: `instafy runtime start`
15
18
  3. Check status / stop:
16
- - `instafy runtime:status`
17
- - `instafy runtime:stop`
19
+ - `instafy runtime status`
20
+ - `instafy runtime stop`
18
21
 
19
22
  ## Common commands
20
23
 
21
- - `instafy runtime:start` — start a local runtime (agent + origin).
22
- - `instafy runtime:status` — show health of the last started runtime.
23
- - `instafy runtime:stop` — stop the last started runtime.
24
- - `instafy tunnel`request a tunnel and forward a local port.
25
- - `instafy api:get` — query controller endpoints (conversations, messages, runs, etc).
24
+ - `instafy runtime start` — start a local runtime (agent + origin).
25
+ - `instafy runtime status` — show health of the last started runtime.
26
+ - `instafy runtime stop` — stop the last started runtime.
27
+ - `instafy git <args...>` run git commands against an Instafy canonical checkout (`.instafy/.git`) when present.
28
+ - `instafy tunnel start` — start a detached tunnel for a local port.
29
+ - `instafy tunnel list` — list local tunnels started by the CLI.
30
+ - `instafy tunnel logs <tunnelId> --follow` — tail tunnel logs.
31
+ - `instafy tunnel stop <tunnelId>` — stop + revoke a tunnel.
32
+ - `instafy api get` — query controller endpoints (conversations, messages, runs, etc).
26
33
 
27
34
  Run `instafy --help` for the full command list and options.
28
35
 
package/dist/api.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import fs from "node:fs";
2
- import { resolveConfiguredAccessToken } from "./config.js";
2
+ import { resolveActiveProfileName, resolveConfiguredAccessToken } from "./config.js";
3
+ import { formatAuthRejectedError } from "./errors.js";
4
+ import { fetchWithControllerAuth } from "./controller-fetch.js";
3
5
  function normalizeUrl(raw) {
4
6
  const value = (raw ?? "").trim();
5
7
  if (!value)
@@ -12,15 +14,31 @@ function normalizeToken(raw) {
12
14
  const trimmed = raw.trim();
13
15
  return trimmed.length ? trimmed : null;
14
16
  }
15
- function resolveBearerToken(options) {
16
- return (normalizeToken(options.accessToken) ??
17
- normalizeToken(options.serviceToken) ??
18
- normalizeToken(process.env["CONTROLLER_ACCESS_TOKEN"]) ??
19
- normalizeToken(process.env["INSTAFY_ACCESS_TOKEN"]) ??
20
- normalizeToken(process.env["INSTAFY_SERVICE_TOKEN"]) ??
21
- normalizeToken(process.env["CONTROLLER_TOKEN"]) ??
22
- normalizeToken(process.env["SUPABASE_ACCESS_TOKEN"]) ??
23
- resolveConfiguredAccessToken());
17
+ function resolveBearerTokenWithSource(options) {
18
+ const cwd = process.cwd();
19
+ const profile = resolveActiveProfileName({ cwd });
20
+ const stored = resolveConfiguredAccessToken({ profile, cwd });
21
+ const explicit = normalizeToken(options.accessToken) ?? normalizeToken(options.serviceToken);
22
+ if (explicit) {
23
+ return { token: explicit, source: "explicit", profile };
24
+ }
25
+ const envKeys = [
26
+ "CONTROLLER_ACCESS_TOKEN",
27
+ "INSTAFY_ACCESS_TOKEN",
28
+ "INSTAFY_SERVICE_TOKEN",
29
+ "CONTROLLER_TOKEN",
30
+ "SUPABASE_ACCESS_TOKEN",
31
+ ];
32
+ for (const key of envKeys) {
33
+ const value = normalizeToken(process.env[key]);
34
+ if (value) {
35
+ return { token: value, source: "env", profile };
36
+ }
37
+ }
38
+ if (stored) {
39
+ return { token: stored, source: "config", profile };
40
+ }
41
+ return { token: null, source: "none", profile };
24
42
  }
25
43
  function parseKeyValue(raw) {
26
44
  const trimmed = raw.trim();
@@ -98,12 +116,10 @@ function maybePrettyPrintJson(text, pretty) {
98
116
  }
99
117
  export async function requestControllerApi(options) {
100
118
  const url = buildRequestUrl(options);
101
- const bearer = resolveBearerToken(options);
119
+ const resolved = resolveBearerTokenWithSource(options);
120
+ const bearer = resolved.token;
102
121
  const headers = new Headers();
103
122
  headers.set("accept", "application/json");
104
- if (bearer) {
105
- headers.set("authorization", `Bearer ${bearer}`);
106
- }
107
123
  for (const header of options.headers ?? []) {
108
124
  const { key, value } = parseHeader(header);
109
125
  headers.set(key, value);
@@ -112,17 +128,35 @@ export async function requestControllerApi(options) {
112
128
  if (body !== undefined && !headers.has("content-type")) {
113
129
  headers.set("content-type", "application/json");
114
130
  }
115
- const response = await fetch(url, {
131
+ const init = {
116
132
  method: options.method,
117
133
  headers,
118
134
  body,
119
- });
135
+ };
136
+ const response = bearer
137
+ ? (await fetchWithControllerAuth({
138
+ url: url.toString(),
139
+ init,
140
+ accessToken: bearer,
141
+ tokenSource: resolved.source,
142
+ profile: resolved.profile,
143
+ cwd: process.cwd(),
144
+ })).response
145
+ : await fetch(url, init);
120
146
  const responseText = await response.text().catch(() => "");
121
147
  const contentType = response.headers.get("content-type") ?? "";
122
148
  const isJson = contentType.includes("application/json") || contentType.includes("+json");
123
149
  const pretty = options.pretty !== false;
124
150
  const formattedBody = isJson ? maybePrettyPrintJson(responseText, pretty) : responseText;
125
151
  if (!response.ok) {
152
+ if (response.status === 401 || response.status === 403) {
153
+ throw formatAuthRejectedError({
154
+ status: response.status,
155
+ responseBody: responseText,
156
+ retryCommand: "instafy login",
157
+ advancedHint: "pass --access-token / --service-token, or set INSTAFY_ACCESS_TOKEN / INSTAFY_SERVICE_TOKEN",
158
+ });
159
+ }
126
160
  const prefix = `Request failed (${response.status} ${response.statusText})`;
127
161
  const suffix = formattedBody.trim() ? `: ${formattedBody}` : "";
128
162
  throw new Error(`${prefix}${suffix}`);