@tokenoftrust/cli 4.2.0 → 4.2.2
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/package.json +1 -1
- package/src/auth.mjs +17 -11
- package/src/commands/feedback.mjs +32 -6
- package/src/commands/submit.mjs +9 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/cli",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.2",
|
|
4
4
|
"description": "Token of Trust developer CLI — clone a tenant store, run it locally with save→reload, and submit it for preview. Installs the `tot` command.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Token of Trust",
|
package/src/auth.mjs
CHANGED
|
@@ -173,18 +173,24 @@ export async function resolveDeveloperSession(client, env, deps = {}) {
|
|
|
173
173
|
{ reason: "expired", hint: developerCredentialRecoveryHint(creds) },
|
|
174
174
|
);
|
|
175
175
|
}
|
|
176
|
+
const refreshGrant = { refreshToken: creds.refreshToken, clientId: creds.clientId };
|
|
176
177
|
let refreshed;
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
178
|
+
// One retry: a single token-endpoint failure is not "you are not signed in".
|
|
179
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
180
|
+
try {
|
|
181
|
+
refreshed = await refreshAccessToken(creds.tokenEndpoint, refreshGrant, fetchImpl);
|
|
182
|
+
break;
|
|
183
|
+
} catch {
|
|
184
|
+
if (attempt === 1) {
|
|
185
|
+
throw new AuthUnavailableError(
|
|
186
|
+
"couldn't refresh your Token of Trust session.",
|
|
187
|
+
{
|
|
188
|
+
reason: "refreshFailed",
|
|
189
|
+
hint: `the refresh failed. ${developerCredentialRecoveryHint(creds)}`,
|
|
190
|
+
},
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
188
194
|
}
|
|
189
195
|
const prior = creds;
|
|
190
196
|
creds = credentialsFromToken({
|
|
@@ -29,6 +29,8 @@ import { fail } from "../errors.mjs";
|
|
|
29
29
|
import { formatDiagnostics, readDiagnostics, recordDiagnostic } from "../diagnostics.mjs";
|
|
30
30
|
|
|
31
31
|
const DEFAULT_MCP_URL = "https://mcp.tokenoftrust.com";
|
|
32
|
+
/** `feedback_submit` returns this field. Example: `fb-1792647485730-7vguok`. */
|
|
33
|
+
const FEEDBACK_ID = /^fb-[a-z0-9]+(?:-[a-z0-9]+)*$/i;
|
|
32
34
|
const TYPES = new Set(["bug", "feature", "improvement"]);
|
|
33
35
|
const SEVERITIES = new Set(["critical", "high", "medium", "low"]);
|
|
34
36
|
const CATEGORIES = new Set([
|
|
@@ -81,6 +83,17 @@ export function buildFeedbackPayload({ type, category, severity, title, descript
|
|
|
81
83
|
};
|
|
82
84
|
}
|
|
83
85
|
|
|
86
|
+
/**
|
|
87
|
+
* The fb- id from a `feedback_submit` result. Only `feedbackId` counts.
|
|
88
|
+
* @param {unknown} result
|
|
89
|
+
* @returns {string|null}
|
|
90
|
+
*/
|
|
91
|
+
export function feedbackReportId(result) {
|
|
92
|
+
if (result == null || typeof result !== "object" || !("feedbackId" in result)) return null;
|
|
93
|
+
const value = result.feedbackId;
|
|
94
|
+
return typeof value === "string" && FEEDBACK_ID.test(value) ? value : null;
|
|
95
|
+
}
|
|
96
|
+
|
|
84
97
|
function parseArgs(argv) {
|
|
85
98
|
const a = {
|
|
86
99
|
mcp: null, type: "improvement", category: "product-ux", severity: "medium",
|
|
@@ -104,8 +117,12 @@ function parseArgs(argv) {
|
|
|
104
117
|
return a;
|
|
105
118
|
}
|
|
106
119
|
|
|
107
|
-
/**
|
|
108
|
-
|
|
120
|
+
/**
|
|
121
|
+
* @param {string[]} argv
|
|
122
|
+
* @param {{ createClient?: typeof createMcpClient, establishSession?: typeof establishSession }} [deps]
|
|
123
|
+
* Test seam. Production calls pass only argv.
|
|
124
|
+
*/
|
|
125
|
+
export async function run(argv, deps = {}) {
|
|
109
126
|
const env = process.env;
|
|
110
127
|
const args = parseArgs(argv);
|
|
111
128
|
if (args.help) {
|
|
@@ -184,12 +201,14 @@ export async function run(argv) {
|
|
|
184
201
|
}
|
|
185
202
|
|
|
186
203
|
const mcpUrl = args.mcp || env.MCP_BASE_URL || env.TOT_MCP_URL || DEFAULT_MCP_URL;
|
|
187
|
-
const
|
|
204
|
+
const createClient = deps.createClient || createMcpClient;
|
|
205
|
+
const openSession = deps.establishSession || establishSession;
|
|
206
|
+
const client = createClient(mcpUrl);
|
|
188
207
|
try {
|
|
189
208
|
// Attach auth in the right order relative to the handshake (developer bearer
|
|
190
209
|
// pre-initialize). We keep the feedback-specific clientInfo by injecting our
|
|
191
210
|
// own initialize.
|
|
192
|
-
await
|
|
211
|
+
await openSession(client, {
|
|
193
212
|
env,
|
|
194
213
|
initialize: () => client.initialize({ name: "tot-cli", version: "feedback" }),
|
|
195
214
|
});
|
|
@@ -204,8 +223,15 @@ export async function run(argv) {
|
|
|
204
223
|
traceId,
|
|
205
224
|
});
|
|
206
225
|
const res = await client.callTool("feedback_submit", payload);
|
|
207
|
-
const id = res
|
|
208
|
-
|
|
226
|
+
const id = feedbackReportId(res);
|
|
227
|
+
if (!id) {
|
|
228
|
+
console.error(fail(
|
|
229
|
+
"the server accepted the note but returned no fb- id.",
|
|
230
|
+
"don't resend yet — the note may already be in the queue.",
|
|
231
|
+
));
|
|
232
|
+
return 1;
|
|
233
|
+
}
|
|
234
|
+
console.log(`\n+ sent — thank you. ${id}`);
|
|
209
235
|
return 0;
|
|
210
236
|
} catch (e) {
|
|
211
237
|
recordDiagnostic(e, { command: "feedback", operation: "feedback_submit", required: true }, env);
|
package/src/commands/submit.mjs
CHANGED
|
@@ -1583,11 +1583,17 @@ export async function run(argv, ctx, {
|
|
|
1583
1583
|
const diagnostic = recordDiagnostic(e, {
|
|
1584
1584
|
command: verb, operation: "session_establish", required: true, degraded: false,
|
|
1585
1585
|
}, env);
|
|
1586
|
-
const
|
|
1587
|
-
|
|
1586
|
+
const authFailure = e instanceof AuthUnavailableError;
|
|
1587
|
+
const note = authFailure
|
|
1588
|
+
? `Candidate not created: ${e.reason}: ${e.message}${e.hint ? ` ${e.hint}` : ""}`
|
|
1588
1589
|
: `Candidate not created: couldn't reach Token of Trust (${describeReadbackError(e)}).`;
|
|
1589
1590
|
if (!args.json) {
|
|
1590
|
-
|
|
1591
|
+
// The hint already states the next step. A second line that always says
|
|
1592
|
+
// `tot login` flattens refreshFailed into "you are not signed in".
|
|
1593
|
+
console.error(fail(
|
|
1594
|
+
note,
|
|
1595
|
+
authFailure ? null : "run `tot login`, then re-run; no candidate ref was pushed",
|
|
1596
|
+
));
|
|
1591
1597
|
console.error(` → ${diagnosticReference(diagnostic, env)}`);
|
|
1592
1598
|
}
|
|
1593
1599
|
emitJson(args, buildJsonResult({
|