@staqon/cli 0.1.0 → 0.1.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 +48 -1
- package/dist/cli.js +27 -6
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -13,6 +13,32 @@ npm install -g @staqon/cli
|
|
|
13
13
|
staqon --version
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
## Getting started
|
|
17
|
+
|
|
18
|
+
1. **The workspace owner creates an API key first.** Open your workspace at
|
|
19
|
+
`<your-workspace-url>/workspace/settings.php?w=<your-workspace-slug>&tab=api` and, under the
|
|
20
|
+
relevant card (Messaging API, Ad API, etc.), generate a key for the team you want the CLI to
|
|
21
|
+
act as. Messaging and Ad keys both require a **Link** key to already exist on that same tab �
|
|
22
|
+
set that up first if you see a "set up Link API first" notice.
|
|
23
|
+
2. On that same `api` tab, turn on **"Allow CLI access"** for the workspace. This is a separate
|
|
24
|
+
switch from the API key above � it must be ON before `staqon login` can be approved. Turning it
|
|
25
|
+
OFF immediately signs out every CLI session for that workspace.
|
|
26
|
+
3. Run `staqon login` from the machine or server you want to use. It prints a URL and a short code.
|
|
27
|
+
4. The **workspace owner** opens that URL (it's also reachable directly at `/cli`) and approves the
|
|
28
|
+
code � they may be asked for their MPIN if their web session is old.
|
|
29
|
+
5. Run `staqon whoami`. It lists every key the CLI can use and which teams hold it. If a command you
|
|
30
|
+
need isn't listed, go back to step 1 for that key type � the CLI itself can never create or see
|
|
31
|
+
API keys, only the owner can.
|
|
32
|
+
6. Send your first message:
|
|
33
|
+
```sh
|
|
34
|
+
staqon message send --email member@example.com -m "Hello from the CLI"
|
|
35
|
+
```
|
|
36
|
+
7. **Member approval (exit code 6).** Most calls that reach a specific member � direct messages,
|
|
37
|
+
comments, internal messages, verification requests � require that member to have approved
|
|
38
|
+
linking with your workspace in their own StaqOn app first. If they haven't, the CLI exits with
|
|
39
|
+
code 6 and a message like "This member hasn't approved linking with this workspace." This isn't
|
|
40
|
+
something the CLI or the owner can bypass � the member has to approve it on their end.
|
|
41
|
+
|
|
16
42
|
## Sign in
|
|
17
43
|
|
|
18
44
|
There is no separate `init` step: `staqon login` is the whole setup.
|
|
@@ -36,9 +62,30 @@ Where the token lives: the OS keychain when available, otherwise a user-only fil
|
|
|
36
62
|
| `staqon refresh` | Rotate the session token (the old one works for 5 more minutes) |
|
|
37
63
|
| `staqon logout` | Forget the local token. Revoking it is done by the owner in the web UI |
|
|
38
64
|
|
|
65
|
+
### Running unattended (a server sending automatic alerts)
|
|
66
|
+
|
|
67
|
+
Session tokens are sliding-expiry: **90 days of inactivity invalidates them, and every call extends
|
|
68
|
+
that window**, up to a hard cap of **1 year from the original login**. For a server that calls the
|
|
69
|
+
CLI regularly (e.g. daily alerts), the token effectively never goes stale on its own � but you should
|
|
70
|
+
still rotate it well before the 90-day mark, in case the server is ever idle for a stretch:
|
|
71
|
+
|
|
72
|
+
- Run `staqon refresh` on a schedule (cron / Task Scheduler) every 30-60 days. It rotates the token;
|
|
73
|
+
the old one keeps working for 5 more minutes, so overlap is safe. Save the new token exactly as
|
|
74
|
+
`staqon` does (OS keychain, or the user-only `credentials.json` file) � don't hardcode it elsewhere.
|
|
75
|
+
- Once a year, at the hard cap, `refresh` will stop working and the command exits with an auth error.
|
|
76
|
+
At that point a human (the workspace owner, or whoever can reach the approval URL) has to run
|
|
77
|
+
`staqon login` again � there is no way around this step by design, since it's what lets the owner
|
|
78
|
+
revoke access.
|
|
79
|
+
- Do **not** try to use the partner's raw API key directly as a substitute for a long-lived token:
|
|
80
|
+
the CLI protocol deliberately never hands the API key to the CLI session at all (see the server
|
|
81
|
+
contract) � the CLI only ever gets a revocable, rate-limited session token, scoped to the CLI's own
|
|
82
|
+
separate limits, so the partner's production API key headroom is never affected by CLI traffic.
|
|
83
|
+
`STAQON_TOKEN` (below) is the only supported "skip login" path, and it still expires the same way.
|
|
84
|
+
|
|
39
85
|
For CI, set `STAQON_TOKEN` instead of logging in (and `STAQON_BASE_URL` if not production). Tokens are
|
|
40
86
|
only ever sent to the host that issued them, and the CLI refuses to send one to a different `--base-url`.
|
|
41
|
-
`refresh` is not available for `STAQON_TOKEN` sessions
|
|
87
|
+
`refresh` is not available for `STAQON_TOKEN` sessions -- if you use this path, you are responsible for
|
|
88
|
+
replacing the value yourself before it expires (same 90-day sliding / 1-year hard cap rules apply).
|
|
42
89
|
|
|
43
90
|
## Teams
|
|
44
91
|
|
package/dist/cli.js
CHANGED
|
@@ -330,15 +330,25 @@ The member has to approve the link in their app; the CLI can't do it for them.`
|
|
|
330
330
|
teams,
|
|
331
331
|
hint: "Eligible teams: " + (teams.map((t) => t.team_slug ?? t.team_id).join(", ") || "(none listed)") + "\nUse --team <team>, or staqon use <team> (staqon use --clear drops a stale default)."
|
|
332
332
|
});
|
|
333
|
-
if (code === "no_key")
|
|
333
|
+
if (code === "no_key") {
|
|
334
|
+
const o = asObj(r.body);
|
|
335
|
+
const surface = typeof o["surface"] === "string" ? o["surface"] : void 0;
|
|
336
|
+
const purpose = typeof o["purpose"] === "string" ? o["purpose"] : void 0;
|
|
337
|
+
const kind = purpose ? `${purpose.charAt(0).toUpperCase()}${purpose.slice(1)}${surface === "internal" ? " (internal)" : ""} key` : "key of that kind";
|
|
334
338
|
throw new ApiError(
|
|
335
|
-
|
|
339
|
+
`This workspace has no active ${kind}.`,
|
|
336
340
|
Exit.TEAM_OR_KEY,
|
|
337
341
|
409,
|
|
338
342
|
r.body,
|
|
339
343
|
r.traceId,
|
|
340
|
-
{
|
|
344
|
+
{
|
|
345
|
+
hint: `The workspace owner creates it at:
|
|
346
|
+
<your-workspace-url>/workspace/settings.php?w=<workspace-slug>&tab=api
|
|
347
|
+
Messaging and Ad keys both require a "Link" key to be set up first on that same tab.
|
|
348
|
+
The CLI itself can never create or see this key.`
|
|
349
|
+
}
|
|
341
350
|
);
|
|
351
|
+
}
|
|
342
352
|
}
|
|
343
353
|
if (r.status === 429) {
|
|
344
354
|
const limit = asObj(r.body)["limit"];
|
|
@@ -579,9 +589,20 @@ function registerAuth(program2) {
|
|
|
579
589
|
const l = b["limits"] ?? {};
|
|
580
590
|
out(`${pc3.bold("Limits")} ${l.used_last_minute ?? "?"}/${l.per_minute ?? "?"} per min, ${l.used_today ?? "?"}/${l.per_day ?? "?"} today`);
|
|
581
591
|
out(pc3.bold("Access"));
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
out(
|
|
592
|
+
const access = b["access"] ?? [];
|
|
593
|
+
if (!access.length) {
|
|
594
|
+
out(pc3.yellow(" No API keys on this workspace yet."));
|
|
595
|
+
out(pc3.dim(" The workspace owner creates one at Settings -> API (tab=api), e.g.:"));
|
|
596
|
+
out(pc3.dim(` ${baseUrlFor()}/workspace/settings.php?w=${ws?.slug ?? "<slug>"}&tab=api`));
|
|
597
|
+
}
|
|
598
|
+
for (const a of access) {
|
|
599
|
+
const teams = (a.teams ?? []).map((t) => t.team_slug ?? t.team_id).join(", ");
|
|
600
|
+
const label = `${a.surface ?? ""}${a.purpose ? ` ${a.purpose}` : ""}`.trim() || "key";
|
|
601
|
+
if (!teams) {
|
|
602
|
+
out(pc3.yellow(` No ${label} key on any team. Create one at Settings -> API.`));
|
|
603
|
+
} else {
|
|
604
|
+
out(` ${label} teams: ${teams}${a.automatic ? " (automatic)" : ""}`);
|
|
605
|
+
}
|
|
585
606
|
if (ctx.verbose) out(pc3.dim(` endpoints: ${(a.endpoints ?? []).join(", ")}`));
|
|
586
607
|
}
|
|
587
608
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@staqon/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "StaqOn command-line client for partner developers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"staqon",
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"test": "node --test test/cli.mock.test.mjs test/validate.test.mjs",
|
|
32
32
|
"pretest:live": "tsup --config tsup.test.config.ts",
|
|
33
33
|
"test:live": "node --test test/identity.live.test.mjs",
|
|
34
|
-
"prepublishOnly": "npm run typecheck && npm run build && npm test"
|
|
34
|
+
"prepublishOnly": "npm run typecheck && npm run build && npm test",
|
|
35
|
+
"prepare": "npm run build"
|
|
35
36
|
},
|
|
36
37
|
"license": "SEE LICENSE IN LICENSE",
|
|
37
38
|
"dependencies": {
|