@theplato/tiro-cli 0.4.1 → 0.6.0
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 +65 -5
- package/dist/bin/tiro.js +177 -1939
- package/package.json +2 -2
package/dist/bin/tiro.js
CHANGED
|
@@ -1,285 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
// src/lib/version.ts
|
|
7
|
-
import { readFileSync } from "fs";
|
|
8
|
-
import { fileURLToPath } from "url";
|
|
9
|
-
import { dirname, resolve } from "path";
|
|
10
|
-
var HERE = dirname(fileURLToPath(import.meta.url));
|
|
11
|
-
var CANDIDATE_PATHS = [
|
|
12
|
-
resolve(HERE, "../../package.json"),
|
|
13
|
-
resolve(HERE, "../../../package.json")
|
|
14
|
-
];
|
|
15
|
-
function readVersion() {
|
|
16
|
-
for (const path of CANDIDATE_PATHS) {
|
|
17
|
-
try {
|
|
18
|
-
const raw = readFileSync(path, "utf8");
|
|
19
|
-
const parsed = JSON.parse(raw);
|
|
20
|
-
if (typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
21
|
-
return parsed.version;
|
|
22
|
-
}
|
|
23
|
-
} catch {
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return "0.0.0-unknown";
|
|
27
|
-
}
|
|
28
|
-
var VERSION = readVersion();
|
|
29
|
-
|
|
30
|
-
// src/lib/error.ts
|
|
31
|
-
var ExitCode = {
|
|
32
|
-
Ok: 0,
|
|
33
|
-
Generic: 1,
|
|
34
|
-
Usage: 2,
|
|
35
|
-
AuthRequired: 4,
|
|
36
|
-
ExUsage: 64,
|
|
37
|
-
ExDataErr: 65,
|
|
38
|
-
ExConfig: 78
|
|
39
|
-
};
|
|
40
|
-
var TiroError = class extends Error {
|
|
41
|
-
code;
|
|
42
|
-
suggestion;
|
|
43
|
-
errorType;
|
|
44
|
-
httpStatus;
|
|
45
|
-
requestId;
|
|
46
|
-
exitCode;
|
|
47
|
-
constructor(payload, exitCode = ExitCode.Generic) {
|
|
48
|
-
super(payload.message);
|
|
49
|
-
this.name = "TiroError";
|
|
50
|
-
this.code = payload.code;
|
|
51
|
-
this.suggestion = payload.suggestion;
|
|
52
|
-
this.errorType = payload.errorType;
|
|
53
|
-
this.httpStatus = payload.httpStatus;
|
|
54
|
-
this.requestId = payload.requestId;
|
|
55
|
-
this.exitCode = exitCode;
|
|
56
|
-
}
|
|
57
|
-
toJSON() {
|
|
58
|
-
return {
|
|
59
|
-
ok: false,
|
|
60
|
-
error: {
|
|
61
|
-
code: this.code,
|
|
62
|
-
message: this.message,
|
|
63
|
-
...this.suggestion !== void 0 && { suggestion: this.suggestion },
|
|
64
|
-
...this.errorType !== void 0 && { errorType: this.errorType },
|
|
65
|
-
...this.httpStatus !== void 0 && { httpStatus: this.httpStatus },
|
|
66
|
-
...this.requestId !== void 0 && { requestId: this.requestId }
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
function authRequired() {
|
|
72
|
-
return new TiroError(
|
|
73
|
-
{
|
|
74
|
-
code: "auth_required",
|
|
75
|
-
message: "Not authenticated. Run `tiro auth login` to sign in, or set TIRO_TOKEN env var.",
|
|
76
|
-
suggestion: "tiro auth login",
|
|
77
|
-
errorType: "auth_required"
|
|
78
|
-
},
|
|
79
|
-
ExitCode.AuthRequired
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// src/lib/output/tty.ts
|
|
84
|
-
function resolveOutputMode(opts) {
|
|
85
|
-
if (opts.json) return "json";
|
|
86
|
-
if (opts.pretty) return "pretty";
|
|
87
|
-
return process.stdout.isTTY ? "pretty" : "json";
|
|
88
|
-
}
|
|
89
|
-
function colorEnabled(opts) {
|
|
90
|
-
if (opts.noColor) return false;
|
|
91
|
-
if (process.env["NO_COLOR"]) return false;
|
|
92
|
-
if (process.env["FORCE_COLOR"]) return true;
|
|
93
|
-
return process.stdout.isTTY === true;
|
|
94
|
-
}
|
|
95
|
-
var ANSI = {
|
|
96
|
-
reset: "\x1B[0m",
|
|
97
|
-
bold: "\x1B[1m",
|
|
98
|
-
dim: "\x1B[2m",
|
|
99
|
-
red: "\x1B[31m",
|
|
100
|
-
green: "\x1B[32m",
|
|
101
|
-
yellow: "\x1B[33m",
|
|
102
|
-
blue: "\x1B[34m",
|
|
103
|
-
magenta: "\x1B[35m",
|
|
104
|
-
cyan: "\x1B[36m",
|
|
105
|
-
gray: "\x1B[90m"
|
|
106
|
-
};
|
|
107
|
-
function color(text, style, opts = {}) {
|
|
108
|
-
if (!colorEnabled(opts)) return text;
|
|
109
|
-
return `${ANSI[style]}${text}${ANSI.reset}`;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// src/lib/output/print.ts
|
|
113
|
-
function printOutput(value, opts = {}) {
|
|
114
|
-
if (opts.quiet) return;
|
|
115
|
-
const mode = resolveOutputMode(opts);
|
|
116
|
-
if (mode === "json") {
|
|
117
|
-
process.stdout.write(`${JSON.stringify(value)}
|
|
118
|
-
`);
|
|
119
|
-
} else {
|
|
120
|
-
process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
121
|
-
`);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
function printError(value) {
|
|
125
|
-
process.stderr.write(`${JSON.stringify(value)}
|
|
126
|
-
`);
|
|
127
|
-
}
|
|
128
|
-
function printNdjson(item) {
|
|
129
|
-
process.stdout.write(`${JSON.stringify(item)}
|
|
130
|
-
`);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// src/commands/auth/index.ts
|
|
134
|
-
import "commander";
|
|
135
|
-
|
|
136
|
-
// src/commands/auth/login.ts
|
|
137
|
-
import "commander";
|
|
138
|
-
|
|
139
|
-
// src/lib/auth/flow.ts
|
|
140
|
-
import { z } from "zod";
|
|
141
|
-
|
|
142
|
-
// src/lib/auth/pkce.ts
|
|
143
|
-
import { createHash, randomBytes, timingSafeEqual } from "crypto";
|
|
144
|
-
function generatePkce() {
|
|
145
|
-
const codeVerifier = base64url(randomBytes(32));
|
|
146
|
-
const codeChallenge = base64url(createHash("sha256").update(codeVerifier).digest());
|
|
147
|
-
return { codeVerifier, codeChallenge, method: "S256" };
|
|
148
|
-
}
|
|
149
|
-
function generateState() {
|
|
150
|
-
return base64url(randomBytes(24));
|
|
151
|
-
}
|
|
152
|
-
function verifyState(received, expected) {
|
|
153
|
-
const a = Buffer.from(received, "utf8");
|
|
154
|
-
const b = Buffer.from(expected, "utf8");
|
|
155
|
-
if (a.length !== b.length) return false;
|
|
156
|
-
return timingSafeEqual(a, b);
|
|
157
|
-
}
|
|
158
|
-
function base64url(buf) {
|
|
159
|
-
return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// src/lib/auth/loopback.ts
|
|
163
|
-
import http from "http";
|
|
164
|
-
|
|
165
|
-
// src/lib/auth/assets.generated.ts
|
|
166
|
-
var TIRO_LOGO_BROWN_DATA_URI = "data:image/png;base64,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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA/78dOBAAAAAAELQ/9SIFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAP3ZeKxkxamrQAAAABJRU5ErkJggg==";
|
|
167
|
-
|
|
168
|
-
// src/lib/auth/loopback.ts
|
|
169
|
-
async function startLoopbackServer() {
|
|
170
|
-
let pendingResolve = null;
|
|
171
|
-
let pendingReject = null;
|
|
172
|
-
const settle = (fn, arg) => {
|
|
173
|
-
pendingResolve = null;
|
|
174
|
-
pendingReject = null;
|
|
175
|
-
fn(arg);
|
|
176
|
-
};
|
|
177
|
-
let consumed = false;
|
|
178
|
-
const server = http.createServer((req, res) => {
|
|
179
|
-
res.setHeader("Connection", "close");
|
|
180
|
-
res.setHeader("Cache-Control", "no-store");
|
|
181
|
-
if (!req.url) {
|
|
182
|
-
res.writeHead(400).end();
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
if (req.method !== "GET") {
|
|
186
|
-
res.writeHead(405, { "Content-Type": "text/plain", Allow: "GET" }).end("Method not allowed");
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
const url = new URL(req.url, `http://127.0.0.1`);
|
|
190
|
-
if (url.pathname !== "/callback") {
|
|
191
|
-
res.writeHead(404, { "Content-Type": "text/plain" }).end("Not found");
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
if (consumed) {
|
|
195
|
-
res.writeHead(410, { "Content-Type": "text/plain" }).end("Callback already consumed");
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
const code = url.searchParams.get("code");
|
|
199
|
-
const state = url.searchParams.get("state");
|
|
200
|
-
const error = url.searchParams.get("error");
|
|
201
|
-
const errorDesc = url.searchParams.get("error_description") ?? "";
|
|
202
|
-
if (error) {
|
|
203
|
-
consumed = true;
|
|
204
|
-
const detail = errorDesc ? ` \u2014 ${errorDesc}` : "";
|
|
205
|
-
respondError(res, 500, `OAuth error: ${error}${detail}`);
|
|
206
|
-
if (pendingReject) settle(pendingReject, new Error(`OAuth error: ${error}${detail}`));
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
if (!code || !state) {
|
|
210
|
-
respondError(res, 400, "Missing `code` or `state` in callback URL.");
|
|
211
|
-
if (pendingReject) settle(pendingReject, new Error("Missing code or state"));
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
consumed = true;
|
|
215
|
-
respondSuccess(res);
|
|
216
|
-
if (pendingResolve) settle(pendingResolve, { code, state });
|
|
217
|
-
});
|
|
218
|
-
await new Promise((resolve4) => {
|
|
219
|
-
server.listen(0, "127.0.0.1", () => resolve4());
|
|
220
|
-
});
|
|
221
|
-
const address = server.address();
|
|
222
|
-
const port = address.port;
|
|
223
|
-
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
224
|
-
return {
|
|
225
|
-
redirectUri,
|
|
226
|
-
port,
|
|
227
|
-
waitForCallback(timeoutMs) {
|
|
228
|
-
return new Promise((resolve4, reject) => {
|
|
229
|
-
const timer = setTimeout(() => {
|
|
230
|
-
pendingResolve = null;
|
|
231
|
-
pendingReject = null;
|
|
232
|
-
reject(new Error(`Timed out waiting for OAuth callback (${timeoutMs}ms)`));
|
|
233
|
-
}, timeoutMs);
|
|
234
|
-
pendingResolve = (r) => {
|
|
235
|
-
clearTimeout(timer);
|
|
236
|
-
resolve4(r);
|
|
237
|
-
};
|
|
238
|
-
pendingReject = (e) => {
|
|
239
|
-
clearTimeout(timer);
|
|
240
|
-
reject(e);
|
|
241
|
-
};
|
|
242
|
-
});
|
|
243
|
-
},
|
|
244
|
-
close() {
|
|
245
|
-
server.close();
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
function escapeHtml(s) {
|
|
250
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
251
|
-
}
|
|
252
|
-
function respondSuccess(res) {
|
|
253
|
-
const html = renderPage({
|
|
254
|
-
kind: "success",
|
|
255
|
-
title: "You're signed in",
|
|
256
|
-
body: "You can close this tab and return to your terminal."
|
|
257
|
-
});
|
|
258
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
259
|
-
res.end(html);
|
|
260
|
-
}
|
|
261
|
-
function respondError(res, status, body) {
|
|
262
|
-
const html = renderPage({
|
|
263
|
-
kind: "error",
|
|
264
|
-
title: "Login failed",
|
|
265
|
-
body
|
|
266
|
-
});
|
|
267
|
-
res.writeHead(status, { "Content-Type": "text/html; charset=utf-8" });
|
|
268
|
-
res.end(html);
|
|
269
|
-
}
|
|
270
|
-
function renderPage(p) {
|
|
271
|
-
const safeTitle = escapeHtml(p.title);
|
|
272
|
-
const safeBody = escapeHtml(p.body);
|
|
273
|
-
const titleColor = p.kind === "error" ? "var(--error)" : "var(--brown-800)";
|
|
274
|
-
const logoOpacity = p.kind === "error" ? "0.4" : "1";
|
|
275
|
-
const closeScript = p.kind === "success" ? `<script>setTimeout(function(){try{window.close();}catch(e){}}, 2000);</script>` : "";
|
|
276
|
-
const hint = p.kind === "success" ? `<p class="hint">Closing this tab automatically…</p>` : "";
|
|
277
|
-
return `<!doctype html>
|
|
2
|
+
import{Command as Kr}from"commander";import{readFileSync as MA}from"fs";import{fileURLToPath as NA}from"url";import{dirname as GA,resolve as me}from"path";var ge=GA(NA(import.meta.url)),VA=[me(ge,"../../package.json"),me(ge,"../../../package.json")];function UA(){for(let e of VA)try{let A=MA(e,"utf8"),t=JSON.parse(A);if(typeof t.version=="string"&&t.version.length>0)return t.version}catch{}return"0.0.0-unknown"}var fe=UA();var u={Ok:0,Generic:1,Usage:2,AuthRequired:4,ExUsage:64,ExDataErr:65,ExConfig:78},f=class extends Error{code;suggestion;errorType;httpStatus;requestId;details;exitCode;constructor(A,t=u.Generic){super(A.message),this.name="TiroError",this.code=A.code,this.suggestion=A.suggestion,this.errorType=A.errorType,this.httpStatus=A.httpStatus,this.requestId=A.requestId,this.details=A.details,this.exitCode=t}toJSON(){return{ok:!1,error:{code:this.code,message:this.message,...this.suggestion!==void 0&&{suggestion:this.suggestion},...this.errorType!==void 0&&{errorType:this.errorType},...this.httpStatus!==void 0&&{httpStatus:this.httpStatus},...this.requestId!==void 0&&{requestId:this.requestId},...this.details!==void 0&&{details:this.details}}}}};function W(){return new f({code:"auth_required",message:"Not authenticated. Run `tiro auth login` to sign in, or set TIRO_TOKEN env var.",suggestion:"tiro auth login",errorType:"auth_required"},u.AuthRequired)}function y(e){return e.json?"json":e.pretty||process.stdout.isTTY?"pretty":"json"}function jA(e){return e.noColor||process.env.NO_COLOR?!1:process.env.FORCE_COLOR?!0:process.stdout.isTTY===!0}var he={reset:"\x1B[0m",bold:"\x1B[1m",dim:"\x1B[2m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",magenta:"\x1B[35m",cyan:"\x1B[36m",gray:"\x1B[90m"};function a(e,A,t={}){return jA(t)?`${he[A]}${e}${he.reset}`:e}function v(e,A={}){if(A.quiet)return;y(A)==="json"?process.stdout.write(`${JSON.stringify(e)}
|
|
3
|
+
`):process.stdout.write(`${JSON.stringify(e,null,2)}
|
|
4
|
+
`)}function te(e){process.stderr.write(`${JSON.stringify(e)}
|
|
5
|
+
`)}function C(e){process.stdout.write(`${JSON.stringify(e)}
|
|
6
|
+
`)}import"commander";import"commander";import{z as B}from"zod";import{createHash as QA,randomBytes as we,timingSafeEqual as zA}from"crypto";function be(){let e=re(we(32)),A=re(QA("sha256").update(e).digest());return{codeVerifier:e,codeChallenge:A,method:"S256"}}function ye(){return re(we(24))}function ke(e,A){let t=Buffer.from(e,"utf8"),r=Buffer.from(A,"utf8");return t.length!==r.length?!1:zA(t,r)}function re(e){return e.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}import YA from"http";var Ce="data:image/png;base64,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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA/78dOBAAAAAAELQ/9SIFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAP3ZeKxkxamrQAAAABJRU5ErkJggg==";async function xe(){let e=null,A=null,t=(d,p)=>{e=null,A=null,d(p)},r=!1,n=YA.createServer((d,p)=>{if(p.setHeader("Connection","close"),p.setHeader("Cache-Control","no-store"),!d.url){p.writeHead(400).end();return}if(d.method!=="GET"){p.writeHead(405,{"Content-Type":"text/plain",Allow:"GET"}).end("Method not allowed");return}let l=new URL(d.url,"http://127.0.0.1");if(l.pathname!=="/callback"){p.writeHead(404,{"Content-Type":"text/plain"}).end("Not found");return}if(r){p.writeHead(410,{"Content-Type":"text/plain"}).end("Callback already consumed");return}let w=l.searchParams.get("code"),b=l.searchParams.get("state"),h=l.searchParams.get("error"),T=l.searchParams.get("error_description")??"";if(h){r=!0;let H=T?` \u2014 ${T}`:"";Te(p,500,`OAuth error: ${h}${H}`),A&&t(A,new Error(`OAuth error: ${h}${H}`));return}if(!w||!b){Te(p,400,"Missing `code` or `state` in callback URL."),A&&t(A,new Error("Missing code or state"));return}r=!0,KA(p),e&&t(e,{code:w,state:b})});await new Promise(d=>{n.listen(0,"127.0.0.1",()=>d())});let s=n.address().port;return{redirectUri:`http://127.0.0.1:${s}/callback`,port:s,waitForCallback(d){return new Promise((p,l)=>{let w=setTimeout(()=>{e=null,A=null,l(new Error(`Timed out waiting for OAuth callback (${d}ms)`))},d);e=b=>{clearTimeout(w),p(b)},A=b=>{clearTimeout(w),l(b)}})},close(){n.close()}}}function ve(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function KA(e){let A=Be({kind:"success",title:"You're signed in",body:"You can close this tab and return to your terminal."});e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(A)}function Te(e,A,t){let r=Be({kind:"error",title:"Login failed",body:t});e.writeHead(A,{"Content-Type":"text/html; charset=utf-8"}),e.end(r)}function Be(e){let A=ve(e.title),t=ve(e.body),r=e.kind==="error"?"var(--error)":"var(--brown-800)",n=e.kind==="error"?"0.4":"1",i=e.kind==="success"?"<script>setTimeout(function(){try{window.close();}catch(e){}}, 2000);</script>":"",s=e.kind==="success"?'<p class="hint">Closing this tab automatically…</p>':"";return`<!doctype html>
|
|
278
7
|
<html lang="en">
|
|
279
8
|
<head>
|
|
280
9
|
<meta charset="utf-8">
|
|
281
10
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
282
|
-
<title>Tiro \u2014 ${
|
|
11
|
+
<title>Tiro \u2014 ${A}</title>
|
|
283
12
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
284
13
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
285
14
|
<link href="https://fonts.googleapis.com/css2?family=Averia+Serif+Libre:wght@300;400;700&display=swap" rel="stylesheet">
|
|
@@ -319,14 +48,14 @@ function renderPage(p) {
|
|
|
319
48
|
height: auto;
|
|
320
49
|
margin-bottom: 1.5rem;
|
|
321
50
|
object-fit: contain;
|
|
322
|
-
opacity: ${
|
|
51
|
+
opacity: ${n};
|
|
323
52
|
}
|
|
324
53
|
h1 {
|
|
325
54
|
font-family: 'Averia Serif Libre', Georgia, 'Times New Roman', serif;
|
|
326
55
|
font-size: 36px;
|
|
327
56
|
line-height: 42px;
|
|
328
57
|
font-weight: 400;
|
|
329
|
-
color: ${
|
|
58
|
+
color: ${r};
|
|
330
59
|
}
|
|
331
60
|
.body {
|
|
332
61
|
margin-top: 1.5rem;
|
|
@@ -355,843 +84,35 @@ function renderPage(p) {
|
|
|
355
84
|
<body>
|
|
356
85
|
<div class="top-spacer"></div>
|
|
357
86
|
<main>
|
|
358
|
-
<img class="logo" src="${
|
|
359
|
-
<h1>${
|
|
360
|
-
<p class="body">${
|
|
361
|
-
${
|
|
87
|
+
<img class="logo" src="${Ce}" alt="Tiro">
|
|
88
|
+
<h1>${A}</h1>
|
|
89
|
+
<p class="body">${t}</p>
|
|
90
|
+
${s}
|
|
362
91
|
</main>
|
|
363
92
|
<footer>© 2026 <a href="https://tiro.ooo">The Plato Inc.</a> — All rights reserved.</footer>
|
|
364
|
-
${
|
|
93
|
+
${i}
|
|
365
94
|
</body>
|
|
366
|
-
</html
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
await open(url, { wait: false });
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
throw new TiroError(
|
|
388
|
-
{
|
|
389
|
-
code: "keychain_write_failed",
|
|
390
|
-
message: `Failed to write to OS keychain: ${err.message}`,
|
|
391
|
-
errorType: "internal_error",
|
|
392
|
-
suggestion: process.platform === "linux" ? "Linux requires a Secret Service daemon (gnome-keyring or kwallet). Or set TIRO_TOKEN env var." : "Check OS keychain permissions, or set TIRO_TOKEN env var."
|
|
393
|
-
},
|
|
394
|
-
ExitCode.Generic
|
|
395
|
-
);
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
function loadToken() {
|
|
399
|
-
const entry = new Entry(SERVICE, ACCOUNT);
|
|
400
|
-
let raw;
|
|
401
|
-
try {
|
|
402
|
-
raw = entry.getPassword();
|
|
403
|
-
} catch {
|
|
404
|
-
return null;
|
|
405
|
-
}
|
|
406
|
-
if (!raw) return null;
|
|
407
|
-
try {
|
|
408
|
-
return JSON.parse(raw);
|
|
409
|
-
} catch {
|
|
410
|
-
return null;
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
function deleteToken() {
|
|
414
|
-
const entry = new Entry(SERVICE, ACCOUNT);
|
|
415
|
-
try {
|
|
416
|
-
return entry.deletePassword();
|
|
417
|
-
} catch {
|
|
418
|
-
return false;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// src/lib/auth/token.ts
|
|
423
|
-
function resolveToken() {
|
|
424
|
-
const envToken = process.env["TIRO_TOKEN"];
|
|
425
|
-
if (envToken) {
|
|
426
|
-
return { accessToken: envToken, source: "env" };
|
|
427
|
-
}
|
|
428
|
-
const stored = loadToken();
|
|
429
|
-
if (stored) {
|
|
430
|
-
return {
|
|
431
|
-
accessToken: stored.accessToken,
|
|
432
|
-
source: "keychain",
|
|
433
|
-
hostname: stored.hostname,
|
|
434
|
-
expiresAt: stored.expiresAt,
|
|
435
|
-
...stored.userId !== void 0 && { userId: stored.userId }
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
return null;
|
|
439
|
-
}
|
|
440
|
-
function decodeJwtPayload(token) {
|
|
441
|
-
const parts = token.split(".");
|
|
442
|
-
if (parts.length !== 3) return null;
|
|
443
|
-
try {
|
|
444
|
-
const payload = parts[1];
|
|
445
|
-
if (!payload) return null;
|
|
446
|
-
const json = Buffer.from(payload, "base64url").toString("utf8");
|
|
447
|
-
return JSON.parse(json);
|
|
448
|
-
} catch {
|
|
449
|
-
return null;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
// src/lib/config.ts
|
|
454
|
-
import Conf from "conf";
|
|
455
|
-
var DEFAULT_HOSTNAME = "https://api.tiro.ooo";
|
|
456
|
-
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]", "::1"]);
|
|
457
|
-
var config = new Conf({
|
|
458
|
-
projectName: "tiro",
|
|
459
|
-
defaults: {
|
|
460
|
-
hostname: DEFAULT_HOSTNAME,
|
|
461
|
-
oauthClientId: null,
|
|
462
|
-
oauthClientIdRegisteredAt: null,
|
|
463
|
-
oauthClientHostname: null,
|
|
464
|
-
defaultOutputDir: null
|
|
465
|
-
}
|
|
466
|
-
});
|
|
467
|
-
function validateHostname(input) {
|
|
468
|
-
const trimmed = input.trim();
|
|
469
|
-
if (trimmed === "") {
|
|
470
|
-
throw hostnameError("empty hostname", input);
|
|
471
|
-
}
|
|
472
|
-
let url;
|
|
473
|
-
try {
|
|
474
|
-
url = new URL(trimmed);
|
|
475
|
-
} catch {
|
|
476
|
-
throw hostnameError("not a valid URL", input);
|
|
477
|
-
}
|
|
478
|
-
if (url.protocol === "https:") {
|
|
479
|
-
} else if (url.protocol === "http:" && isLoopback(url.hostname)) {
|
|
480
|
-
} else {
|
|
481
|
-
throw hostnameError(
|
|
482
|
-
`disallowed scheme "${url.protocol}" \u2014 only https:// or http://localhost is permitted`,
|
|
483
|
-
input
|
|
484
|
-
);
|
|
485
|
-
}
|
|
486
|
-
if (url.username !== "" || url.password !== "") {
|
|
487
|
-
throw hostnameError("URL must not embed credentials", input);
|
|
488
|
-
}
|
|
489
|
-
if (url.search !== "" || url.hash !== "") {
|
|
490
|
-
throw hostnameError("URL must not include query or fragment", input);
|
|
491
|
-
}
|
|
492
|
-
if (url.pathname !== "" && url.pathname !== "/") {
|
|
493
|
-
throw hostnameError("URL must be host root (no path segment)", input);
|
|
494
|
-
}
|
|
495
|
-
const origin = url.origin;
|
|
496
|
-
return stripTrailingSlash(origin);
|
|
497
|
-
}
|
|
498
|
-
function isLoopback(host) {
|
|
499
|
-
if (host.startsWith("[") && host.endsWith("]")) {
|
|
500
|
-
host = host.slice(1, -1);
|
|
501
|
-
}
|
|
502
|
-
return LOOPBACK_HOSTS.has(host);
|
|
503
|
-
}
|
|
504
|
-
function hostnameError(reason, input) {
|
|
505
|
-
return new TiroError(
|
|
506
|
-
{
|
|
507
|
-
code: "invalid_hostname",
|
|
508
|
-
message: `Refusing to use hostname "${input}": ${reason}.`,
|
|
509
|
-
errorType: "bad_request",
|
|
510
|
-
suggestion: "Use https://<host> (or http://localhost for local dev). If TIRO_HOSTNAME is set in your environment, unset it."
|
|
511
|
-
},
|
|
512
|
-
ExitCode.Usage
|
|
513
|
-
);
|
|
514
|
-
}
|
|
515
|
-
function getHostname(override) {
|
|
516
|
-
if (override !== void 0) return validateHostname(override);
|
|
517
|
-
const env = process.env["TIRO_HOSTNAME"];
|
|
518
|
-
if (env) return validateHostname(env);
|
|
519
|
-
return validateHostname(config.get("hostname"));
|
|
520
|
-
}
|
|
521
|
-
function getOauthClientId(hostname) {
|
|
522
|
-
const id = config.get("oauthClientId");
|
|
523
|
-
const registeredAt = config.get("oauthClientIdRegisteredAt");
|
|
524
|
-
const cachedHostname = config.get("oauthClientHostname");
|
|
525
|
-
if (!id || !registeredAt) return null;
|
|
526
|
-
if (cachedHostname !== hostname) return null;
|
|
527
|
-
const ttlMs = 29 * 24 * 60 * 60 * 1e3;
|
|
528
|
-
if (Date.now() - registeredAt > ttlMs) return null;
|
|
529
|
-
return id;
|
|
530
|
-
}
|
|
531
|
-
function setOauthClientId(clientId, hostname) {
|
|
532
|
-
config.set("oauthClientId", clientId);
|
|
533
|
-
config.set("oauthClientIdRegisteredAt", Date.now());
|
|
534
|
-
config.set("oauthClientHostname", hostname);
|
|
535
|
-
}
|
|
536
|
-
function clearOauthClientId() {
|
|
537
|
-
config.set("oauthClientId", null);
|
|
538
|
-
config.set("oauthClientIdRegisteredAt", null);
|
|
539
|
-
config.set("oauthClientHostname", null);
|
|
540
|
-
}
|
|
541
|
-
function stripTrailingSlash(s) {
|
|
542
|
-
return s.endsWith("/") ? s.slice(0, -1) : s;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
// src/lib/auth/flow.ts
|
|
546
|
-
var RegisterResponseSchema = z.object({
|
|
547
|
-
client_id: z.string(),
|
|
548
|
-
client_secret: z.string().optional()
|
|
549
|
-
});
|
|
550
|
-
var TokenResponseSchema = z.object({
|
|
551
|
-
access_token: z.string(),
|
|
552
|
-
token_type: z.string(),
|
|
553
|
-
expires_in: z.number().optional(),
|
|
554
|
-
scope: z.string().optional()
|
|
555
|
-
});
|
|
556
|
-
var CALLBACK_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
557
|
-
var DEFAULT_SCOPE = "mcp:notes:read";
|
|
558
|
-
async function performLogin(options = {}) {
|
|
559
|
-
const hostname = getHostname(options.hostname);
|
|
560
|
-
const onPrompt = options.onPrompt ?? ((msg) => process.stderr.write(`${msg}
|
|
561
|
-
`));
|
|
562
|
-
const loopback = await startLoopbackServer();
|
|
563
|
-
try {
|
|
564
|
-
const clientId = await ensureOauthClient(hostname, loopback.redirectUri);
|
|
565
|
-
const { codeVerifier, codeChallenge } = generatePkce();
|
|
566
|
-
const state = generateState();
|
|
567
|
-
const authorizeUrl = buildAuthorizeUrl({
|
|
568
|
-
hostname,
|
|
569
|
-
clientId,
|
|
570
|
-
redirectUri: loopback.redirectUri,
|
|
571
|
-
state,
|
|
572
|
-
codeChallenge,
|
|
573
|
-
scope: options.scope ?? DEFAULT_SCOPE
|
|
574
|
-
});
|
|
575
|
-
if (options.noBrowser) {
|
|
576
|
-
onPrompt(`Open this URL in your browser:
|
|
577
|
-
${authorizeUrl}`);
|
|
578
|
-
} else {
|
|
579
|
-
onPrompt(`Opening browser for sign-in...`);
|
|
580
|
-
onPrompt(`If the browser does not open, visit:
|
|
581
|
-
${authorizeUrl}`);
|
|
582
|
-
await openBrowser(authorizeUrl);
|
|
583
|
-
}
|
|
584
|
-
const callback = await loopback.waitForCallback(CALLBACK_TIMEOUT_MS);
|
|
585
|
-
if (!verifyState(callback.state, state)) {
|
|
586
|
-
throw new TiroError(
|
|
587
|
-
{
|
|
588
|
-
code: "auth_state_mismatch",
|
|
589
|
-
message: "OAuth state mismatch \u2014 possible CSRF. Aborting.",
|
|
590
|
-
errorType: "unauthorized"
|
|
591
|
-
},
|
|
592
|
-
ExitCode.Generic
|
|
593
|
-
);
|
|
594
|
-
}
|
|
595
|
-
const tokenRes = await exchangeToken({
|
|
596
|
-
hostname,
|
|
597
|
-
clientId,
|
|
598
|
-
code: callback.code,
|
|
599
|
-
redirectUri: loopback.redirectUri,
|
|
600
|
-
codeVerifier
|
|
601
|
-
});
|
|
602
|
-
const expiresAt = computeExpiry(tokenRes.expires_in);
|
|
603
|
-
const payload = decodeJwtPayload(tokenRes.access_token);
|
|
604
|
-
const userId = typeof payload?.["sub"] === "string" ? payload["sub"] : void 0;
|
|
605
|
-
const stored = {
|
|
606
|
-
accessToken: tokenRes.access_token,
|
|
607
|
-
tokenType: tokenRes.token_type,
|
|
608
|
-
expiresAt,
|
|
609
|
-
hostname,
|
|
610
|
-
...tokenRes.scope !== void 0 && { scope: tokenRes.scope },
|
|
611
|
-
...userId !== void 0 && { userId }
|
|
612
|
-
};
|
|
613
|
-
saveToken(stored);
|
|
614
|
-
return { hostname, userId, expiresAt };
|
|
615
|
-
} finally {
|
|
616
|
-
loopback.close();
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
async function ensureOauthClient(hostname, redirectUri) {
|
|
620
|
-
const cached = getOauthClientId(hostname);
|
|
621
|
-
if (cached) return cached;
|
|
622
|
-
const url = `${hostname}/v1/mcp/oauth/register`;
|
|
623
|
-
let res;
|
|
624
|
-
try {
|
|
625
|
-
res = await fetch(url, {
|
|
626
|
-
method: "POST",
|
|
627
|
-
headers: { "Content-Type": "application/json" },
|
|
628
|
-
body: JSON.stringify({
|
|
629
|
-
client_name: "tiro-cli",
|
|
630
|
-
redirect_uris: [redirectUri],
|
|
631
|
-
grant_types: ["authorization_code"],
|
|
632
|
-
response_types: ["code"],
|
|
633
|
-
token_endpoint_auth_method: "none",
|
|
634
|
-
scope: DEFAULT_SCOPE
|
|
635
|
-
})
|
|
636
|
-
});
|
|
637
|
-
} catch (err) {
|
|
638
|
-
throw new TiroError(
|
|
639
|
-
{
|
|
640
|
-
code: "network_error",
|
|
641
|
-
message: `Failed to reach ${hostname}: ${err.message}`,
|
|
642
|
-
errorType: "network_error",
|
|
643
|
-
suggestion: "Check your network connection or --hostname."
|
|
644
|
-
},
|
|
645
|
-
ExitCode.Generic
|
|
646
|
-
);
|
|
647
|
-
}
|
|
648
|
-
if (!res.ok) {
|
|
649
|
-
const detail = await safeText(res);
|
|
650
|
-
throw new TiroError(
|
|
651
|
-
{
|
|
652
|
-
code: "oauth_register_failed",
|
|
653
|
-
message: `Dynamic Client Registration failed: HTTP ${res.status}`,
|
|
654
|
-
errorType: "internal_error",
|
|
655
|
-
httpStatus: res.status,
|
|
656
|
-
...detail !== "" && { suggestion: detail.slice(0, 200) }
|
|
657
|
-
},
|
|
658
|
-
ExitCode.Generic
|
|
659
|
-
);
|
|
660
|
-
}
|
|
661
|
-
const json = await res.json();
|
|
662
|
-
const parsed = RegisterResponseSchema.safeParse(json);
|
|
663
|
-
if (!parsed.success) {
|
|
664
|
-
throw new TiroError(
|
|
665
|
-
{
|
|
666
|
-
code: "oauth_register_invalid",
|
|
667
|
-
message: "Registration response did not match expected shape.",
|
|
668
|
-
errorType: "internal_error"
|
|
669
|
-
},
|
|
670
|
-
ExitCode.Generic
|
|
671
|
-
);
|
|
672
|
-
}
|
|
673
|
-
setOauthClientId(parsed.data.client_id, hostname);
|
|
674
|
-
return parsed.data.client_id;
|
|
675
|
-
}
|
|
676
|
-
function buildAuthorizeUrl(input) {
|
|
677
|
-
const u = new URL(`${input.hostname}/v1/mcp/oauth/authorize`);
|
|
678
|
-
u.searchParams.set("response_type", "code");
|
|
679
|
-
u.searchParams.set("client_id", input.clientId);
|
|
680
|
-
u.searchParams.set("redirect_uri", input.redirectUri);
|
|
681
|
-
u.searchParams.set("state", input.state);
|
|
682
|
-
u.searchParams.set("code_challenge", input.codeChallenge);
|
|
683
|
-
u.searchParams.set("code_challenge_method", "S256");
|
|
684
|
-
u.searchParams.set("scope", input.scope);
|
|
685
|
-
return u.toString();
|
|
686
|
-
}
|
|
687
|
-
async function exchangeToken(input) {
|
|
688
|
-
const url = `${input.hostname}/v1/mcp/oauth/token`;
|
|
689
|
-
const body = new URLSearchParams({
|
|
690
|
-
grant_type: "authorization_code",
|
|
691
|
-
code: input.code,
|
|
692
|
-
redirect_uri: input.redirectUri,
|
|
693
|
-
client_id: input.clientId,
|
|
694
|
-
code_verifier: input.codeVerifier
|
|
695
|
-
});
|
|
696
|
-
let res;
|
|
697
|
-
try {
|
|
698
|
-
res = await fetch(url, {
|
|
699
|
-
method: "POST",
|
|
700
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
701
|
-
body: body.toString()
|
|
702
|
-
});
|
|
703
|
-
} catch (err) {
|
|
704
|
-
throw new TiroError(
|
|
705
|
-
{
|
|
706
|
-
code: "network_error",
|
|
707
|
-
message: `Failed to reach ${input.hostname}: ${err.message}`,
|
|
708
|
-
errorType: "network_error"
|
|
709
|
-
},
|
|
710
|
-
ExitCode.Generic
|
|
711
|
-
);
|
|
712
|
-
}
|
|
713
|
-
if (!res.ok) {
|
|
714
|
-
if (res.status === 400 || res.status === 401) {
|
|
715
|
-
clearOauthClientId();
|
|
716
|
-
}
|
|
717
|
-
const detail = await safeText(res);
|
|
718
|
-
throw new TiroError(
|
|
719
|
-
{
|
|
720
|
-
code: "oauth_token_failed",
|
|
721
|
-
message: `Token exchange failed: HTTP ${res.status}`,
|
|
722
|
-
errorType: "unauthorized",
|
|
723
|
-
httpStatus: res.status,
|
|
724
|
-
...detail !== "" && { suggestion: detail.slice(0, 200) }
|
|
725
|
-
},
|
|
726
|
-
ExitCode.AuthRequired
|
|
727
|
-
);
|
|
728
|
-
}
|
|
729
|
-
const json = await res.json();
|
|
730
|
-
const parsed = TokenResponseSchema.safeParse(json);
|
|
731
|
-
if (!parsed.success) {
|
|
732
|
-
throw new TiroError(
|
|
733
|
-
{
|
|
734
|
-
code: "oauth_token_invalid",
|
|
735
|
-
message: "Token response did not match expected shape.",
|
|
736
|
-
errorType: "internal_error"
|
|
737
|
-
},
|
|
738
|
-
ExitCode.Generic
|
|
739
|
-
);
|
|
740
|
-
}
|
|
741
|
-
return parsed.data;
|
|
742
|
-
}
|
|
743
|
-
function computeExpiry(expiresIn) {
|
|
744
|
-
const fallbackSeconds = 180 * 24 * 60 * 60;
|
|
745
|
-
const seconds = expiresIn ?? fallbackSeconds;
|
|
746
|
-
return Date.now() + seconds * 1e3;
|
|
747
|
-
}
|
|
748
|
-
async function safeText(res) {
|
|
749
|
-
try {
|
|
750
|
-
return await res.text();
|
|
751
|
-
} catch {
|
|
752
|
-
return "";
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
// src/commands/auth/login.ts
|
|
757
|
-
function registerAuthLogin(parent) {
|
|
758
|
-
parent.command("login").description("Sign in to Tiro via OAuth (browser-based, PKCE)").option("--hostname <url>", "API base URL (overrides config / TIRO_HOSTNAME)").option("--no-browser", "Print the URL instead of opening a browser").action(async (opts, cmd) => {
|
|
759
|
-
const globalOpts = cmd.optsWithGlobals();
|
|
760
|
-
const result = await performLogin({
|
|
761
|
-
...opts.hostname !== void 0 && { hostname: opts.hostname },
|
|
762
|
-
noBrowser: opts.noBrowser === true,
|
|
763
|
-
onPrompt: (msg) => {
|
|
764
|
-
if (globalOpts.quiet) return;
|
|
765
|
-
process.stderr.write(`${color(msg, "cyan", globalOpts)}
|
|
766
|
-
`);
|
|
767
|
-
}
|
|
768
|
-
});
|
|
769
|
-
const hostname = getHostname(opts.hostname);
|
|
770
|
-
const expiresIso = new Date(result.expiresAt).toISOString();
|
|
771
|
-
if (globalOpts.json) {
|
|
772
|
-
printOutput(
|
|
773
|
-
{
|
|
774
|
-
ok: true,
|
|
775
|
-
data: {
|
|
776
|
-
signedIn: true,
|
|
777
|
-
hostname,
|
|
778
|
-
userId: result.userId ?? null,
|
|
779
|
-
expiresAt: expiresIso
|
|
780
|
-
}
|
|
781
|
-
},
|
|
782
|
-
globalOpts
|
|
783
|
-
);
|
|
784
|
-
} else if (!globalOpts.quiet) {
|
|
785
|
-
process.stderr.write(`${color("\u2713", "green", globalOpts)} Signed in to ${hostname}
|
|
786
|
-
`);
|
|
787
|
-
if (result.userId) {
|
|
788
|
-
process.stderr.write(` user: ${result.userId}
|
|
789
|
-
`);
|
|
790
|
-
}
|
|
791
|
-
process.stderr.write(` token expires: ${expiresIso}
|
|
792
|
-
`);
|
|
793
|
-
}
|
|
794
|
-
});
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
// src/commands/auth/status.ts
|
|
798
|
-
import "commander";
|
|
799
|
-
function registerAuthStatus(parent) {
|
|
800
|
-
parent.command("status").description("Show current authenticated account and scopes").action(async (_opts, cmd) => {
|
|
801
|
-
const globalOpts = cmd.optsWithGlobals();
|
|
802
|
-
const t = resolveToken();
|
|
803
|
-
if (!t) {
|
|
804
|
-
throw authRequired();
|
|
805
|
-
}
|
|
806
|
-
const payload = decodeJwtPayload(t.accessToken);
|
|
807
|
-
const sub = typeof payload?.["sub"] === "string" ? payload["sub"] : null;
|
|
808
|
-
const exp = typeof payload?.["exp"] === "number" ? payload["exp"] * 1e3 : null;
|
|
809
|
-
const scope = typeof payload?.["scope"] === "string" ? payload["scope"] : null;
|
|
810
|
-
const expMs = exp ?? t.expiresAt ?? null;
|
|
811
|
-
const expired = expMs !== null && Date.now() >= expMs;
|
|
812
|
-
const report = {
|
|
813
|
-
signedIn: true,
|
|
814
|
-
source: t.source,
|
|
815
|
-
hostname: t.hostname ?? null,
|
|
816
|
-
userId: t.userId ?? sub,
|
|
817
|
-
scope,
|
|
818
|
-
expiresAt: expMs ? new Date(expMs).toISOString() : null,
|
|
819
|
-
expired,
|
|
820
|
-
tokenPrefix: `${t.accessToken.slice(0, 4)}...***`
|
|
821
|
-
};
|
|
822
|
-
if (globalOpts.json || !process.stdout.isTTY) {
|
|
823
|
-
printOutput({ ok: true, data: report }, globalOpts);
|
|
824
|
-
} else {
|
|
825
|
-
const headIcon = expired ? color("!", "yellow", globalOpts) : color("\u2713", "green", globalOpts);
|
|
826
|
-
const headText = expired ? "Token expired" : "Signed in";
|
|
827
|
-
process.stdout.write(`${headIcon} ${headText}
|
|
828
|
-
`);
|
|
829
|
-
process.stdout.write(` source: ${report.source}
|
|
830
|
-
`);
|
|
831
|
-
if (report.hostname) process.stdout.write(` hostname: ${report.hostname}
|
|
832
|
-
`);
|
|
833
|
-
if (report.userId) process.stdout.write(` user: ${report.userId}
|
|
834
|
-
`);
|
|
835
|
-
if (report.scope) process.stdout.write(` scope: ${report.scope}
|
|
836
|
-
`);
|
|
837
|
-
if (report.expiresAt) {
|
|
838
|
-
const tag = expired ? color(" (expired)", "red", globalOpts) : "";
|
|
839
|
-
process.stdout.write(` expires at: ${report.expiresAt}${tag}
|
|
840
|
-
`);
|
|
841
|
-
}
|
|
842
|
-
process.stdout.write(` token: ${report.tokenPrefix}
|
|
843
|
-
`);
|
|
844
|
-
if (expired) {
|
|
845
|
-
process.stdout.write(
|
|
846
|
-
`
|
|
847
|
-
${color("\u2192", "gray", globalOpts)} Run \`tiro auth login\` to refresh.
|
|
848
|
-
`
|
|
849
|
-
);
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
});
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
// src/commands/auth/logout.ts
|
|
856
|
-
import "commander";
|
|
857
|
-
function registerAuthLogout(parent) {
|
|
858
|
-
parent.command("logout").description("Sign out and clear the stored token").action(async (_opts, cmd) => {
|
|
859
|
-
const globalOpts = cmd.optsWithGlobals();
|
|
860
|
-
const removed = deleteToken();
|
|
861
|
-
clearOauthClientId();
|
|
862
|
-
if (globalOpts.json) {
|
|
863
|
-
printOutput({ ok: true, data: { signedOut: true, hadToken: removed } }, globalOpts);
|
|
864
|
-
} else if (!globalOpts.quiet) {
|
|
865
|
-
if (removed) {
|
|
866
|
-
process.stderr.write(`${color("\u2713", "green", globalOpts)} Signed out
|
|
867
|
-
`);
|
|
868
|
-
} else {
|
|
869
|
-
process.stderr.write(`${color("\u2022", "gray", globalOpts)} No token was stored
|
|
870
|
-
`);
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
});
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
// src/commands/auth/index.ts
|
|
877
|
-
function registerAuth(program) {
|
|
878
|
-
const auth = program.command("auth").description("Manage authentication");
|
|
879
|
-
registerAuthLogin(auth);
|
|
880
|
-
registerAuthStatus(auth);
|
|
881
|
-
registerAuthLogout(auth);
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
// src/commands/notes/index.ts
|
|
885
|
-
import "commander";
|
|
886
|
-
|
|
887
|
-
// src/commands/notes/list.ts
|
|
888
|
-
import "commander";
|
|
889
|
-
|
|
890
|
-
// src/lib/api/client.ts
|
|
891
|
-
import "zod";
|
|
892
|
-
|
|
893
|
-
// src/lib/api/types.ts
|
|
894
|
-
import { z as z2 } from "zod";
|
|
895
|
-
var CollaboratorSchema = z2.object({
|
|
896
|
-
guid: z2.string(),
|
|
897
|
-
name: z2.string(),
|
|
898
|
-
email: z2.string(),
|
|
899
|
-
role: z2.enum(["OWNER", "EDITOR", "VIEWER"])
|
|
900
|
-
});
|
|
901
|
-
var ParticipantSchema = z2.object({
|
|
902
|
-
name: z2.string().nullable().optional(),
|
|
903
|
-
email: z2.string().nullable().optional()
|
|
904
|
-
});
|
|
905
|
-
var NoteSchema = z2.object({
|
|
906
|
-
guid: z2.string(),
|
|
907
|
-
title: z2.string(),
|
|
908
|
-
createdAt: z2.string(),
|
|
909
|
-
updatedAt: z2.string(),
|
|
910
|
-
sourceType: z2.string(),
|
|
911
|
-
recordingDurationSeconds: z2.number(),
|
|
912
|
-
collaborators: z2.array(CollaboratorSchema).optional().default([]),
|
|
913
|
-
participants: z2.array(ParticipantSchema).optional().default([]),
|
|
914
|
-
webUrl: z2.string(),
|
|
915
|
-
recordingStartAt: z2.string().nullable().optional(),
|
|
916
|
-
recordingEndAt: z2.string().nullable().optional()
|
|
917
|
-
}).passthrough();
|
|
918
|
-
var TextObjectSchema = z2.object({
|
|
919
|
-
type: z2.string(),
|
|
920
|
-
content: z2.string()
|
|
921
|
-
});
|
|
922
|
-
var SpeakerInfoSchema = z2.object({
|
|
923
|
-
label: z2.string(),
|
|
924
|
-
personName: z2.string().nullable().optional()
|
|
925
|
-
});
|
|
926
|
-
var DiarizedSegmentSchema = z2.object({
|
|
927
|
-
content: z2.string(),
|
|
928
|
-
speaker: SpeakerInfoSchema
|
|
929
|
-
});
|
|
930
|
-
var ParagraphSchema = z2.object({
|
|
931
|
-
uuid: z2.string(),
|
|
932
|
-
transcribeLocale: z2.string().nullable().optional(),
|
|
933
|
-
transcript: TextObjectSchema.nullable().optional(),
|
|
934
|
-
diarizedSegments: z2.array(DiarizedSegmentSchema).nullable().optional(),
|
|
935
|
-
timeFrom: z2.string().nullable().optional(),
|
|
936
|
-
timeTo: z2.string().nullable().optional(),
|
|
937
|
-
locked: z2.boolean().optional()
|
|
938
|
-
}).passthrough();
|
|
939
|
-
var McpSegmentSchema = z2.object({
|
|
940
|
-
content: z2.string(),
|
|
941
|
-
speaker: z2.object({
|
|
942
|
-
label: z2.string(),
|
|
943
|
-
name: z2.string().nullable()
|
|
944
|
-
}).nullable()
|
|
945
|
-
});
|
|
946
|
-
var McpParagraphSchema = z2.object({
|
|
947
|
-
timeFrom: z2.string().nullable(),
|
|
948
|
-
timeTo: z2.string().nullable(),
|
|
949
|
-
segments: z2.array(McpSegmentSchema)
|
|
950
|
-
});
|
|
951
|
-
var McpTranscriptSchema = z2.object({
|
|
952
|
-
noteGuid: z2.string(),
|
|
953
|
-
title: z2.string(),
|
|
954
|
-
participants: z2.array(z2.string()),
|
|
955
|
-
createdAt: z2.string(),
|
|
956
|
-
recordingDurationSeconds: z2.number(),
|
|
957
|
-
paragraphs: z2.array(McpParagraphSchema)
|
|
958
|
-
});
|
|
959
|
-
var PageCursorResponseSchema = (item) => z2.object({
|
|
960
|
-
content: z2.array(item),
|
|
961
|
-
nextCursor: z2.string().nullable()
|
|
962
|
-
});
|
|
963
|
-
var SimpleListResponseSchema = (item) => z2.object({
|
|
964
|
-
content: z2.array(item)
|
|
965
|
-
});
|
|
966
|
-
var ApiErrorSchema = z2.object({
|
|
967
|
-
error: z2.object({
|
|
968
|
-
code: z2.number(),
|
|
969
|
-
errorType: z2.string(),
|
|
970
|
-
message: z2.string(),
|
|
971
|
-
detail: z2.string().nullable().optional()
|
|
972
|
-
})
|
|
973
|
-
});
|
|
974
|
-
|
|
975
|
-
// src/lib/api/client.ts
|
|
976
|
-
var TiroApiClient = class {
|
|
977
|
-
constructor(hostname, token) {
|
|
978
|
-
this.hostname = hostname;
|
|
979
|
-
this.token = token;
|
|
980
|
-
}
|
|
981
|
-
hostname;
|
|
982
|
-
token;
|
|
983
|
-
async getJson(path, schema, params) {
|
|
984
|
-
const url = this.buildUrl(path, params);
|
|
985
|
-
const res = await this.fetch(url, { method: "GET" });
|
|
986
|
-
return this.parseJson(res, schema, "GET", path);
|
|
987
|
-
}
|
|
988
|
-
async postJson(path, schema, body) {
|
|
989
|
-
const url = this.buildUrl(path);
|
|
990
|
-
const res = await this.fetch(url, {
|
|
991
|
-
method: "POST",
|
|
992
|
-
headers: { "Content-Type": "application/json" },
|
|
993
|
-
body: JSON.stringify(body)
|
|
994
|
-
});
|
|
995
|
-
return this.parseJson(res, schema, "POST", path);
|
|
996
|
-
}
|
|
997
|
-
async putJson(path, schema, body) {
|
|
998
|
-
const url = this.buildUrl(path);
|
|
999
|
-
const res = await this.fetch(url, {
|
|
1000
|
-
method: "PUT",
|
|
1001
|
-
headers: { "Content-Type": "application/json" },
|
|
1002
|
-
body: JSON.stringify(body)
|
|
1003
|
-
});
|
|
1004
|
-
return this.parseJson(res, schema, "PUT", path);
|
|
1005
|
-
}
|
|
1006
|
-
async deleteVoid(path) {
|
|
1007
|
-
const url = this.buildUrl(path);
|
|
1008
|
-
const res = await this.fetch(url, { method: "DELETE" });
|
|
1009
|
-
if (!res.ok) throw await mapHttpError(res, "DELETE", path);
|
|
1010
|
-
}
|
|
1011
|
-
buildUrl(path, params) {
|
|
1012
|
-
return buildApiUrl(this.hostname, path, params);
|
|
1013
|
-
}
|
|
1014
|
-
async fetch(url, init) {
|
|
1015
|
-
const headers = new Headers(init.headers);
|
|
1016
|
-
headers.set("Authorization", `Bearer ${this.token}`);
|
|
1017
|
-
headers.set("Accept", "application/json");
|
|
1018
|
-
try {
|
|
1019
|
-
return await fetch(url, { ...init, headers });
|
|
1020
|
-
} catch (err) {
|
|
1021
|
-
throw new TiroError(
|
|
1022
|
-
{
|
|
1023
|
-
code: "network_error",
|
|
1024
|
-
message: `Network error reaching ${this.hostname}: ${err.message}`,
|
|
1025
|
-
errorType: "network_error",
|
|
1026
|
-
suggestion: "Check your network or --hostname."
|
|
1027
|
-
},
|
|
1028
|
-
ExitCode.Generic
|
|
1029
|
-
);
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
async parseJson(res, schema, method, path) {
|
|
1033
|
-
if (!res.ok) throw await mapHttpError(res, method, path);
|
|
1034
|
-
let json;
|
|
1035
|
-
try {
|
|
1036
|
-
json = await res.json();
|
|
1037
|
-
} catch (err) {
|
|
1038
|
-
throw new TiroError(
|
|
1039
|
-
{
|
|
1040
|
-
code: "invalid_response",
|
|
1041
|
-
message: `Failed to parse JSON from ${method} ${path}: ${err.message}`,
|
|
1042
|
-
errorType: "internal_error"
|
|
1043
|
-
},
|
|
1044
|
-
ExitCode.Generic
|
|
1045
|
-
);
|
|
1046
|
-
}
|
|
1047
|
-
const parsed = schema.safeParse(json);
|
|
1048
|
-
if (!parsed.success) {
|
|
1049
|
-
throw new TiroError(
|
|
1050
|
-
{
|
|
1051
|
-
code: "schema_mismatch",
|
|
1052
|
-
message: `Response shape did not match expected schema (${method} ${path}).`,
|
|
1053
|
-
errorType: "internal_error",
|
|
1054
|
-
suggestion: parsed.error.issues.slice(0, 3).map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")
|
|
1055
|
-
},
|
|
1056
|
-
ExitCode.Generic
|
|
1057
|
-
);
|
|
1058
|
-
}
|
|
1059
|
-
return parsed.data;
|
|
1060
|
-
}
|
|
1061
|
-
};
|
|
1062
|
-
async function mapHttpError(res, method, path) {
|
|
1063
|
-
const requestId = res.headers.get("x-request-id") ?? void 0;
|
|
1064
|
-
const exitCode = res.status === 401 ? ExitCode.AuthRequired : ExitCode.Generic;
|
|
1065
|
-
const apiError = await tryParseApiError(res);
|
|
1066
|
-
if (apiError) {
|
|
1067
|
-
return new TiroError(
|
|
1068
|
-
{
|
|
1069
|
-
code: `${apiError.error.errorType}`,
|
|
1070
|
-
message: apiError.error.message,
|
|
1071
|
-
errorType: apiError.error.errorType,
|
|
1072
|
-
httpStatus: res.status,
|
|
1073
|
-
...requestId !== void 0 && { requestId },
|
|
1074
|
-
...res.status === 401 && {
|
|
1075
|
-
suggestion: "Run `tiro auth login` to refresh."
|
|
1076
|
-
}
|
|
1077
|
-
},
|
|
1078
|
-
exitCode
|
|
1079
|
-
);
|
|
1080
|
-
}
|
|
1081
|
-
return new TiroError(
|
|
1082
|
-
{
|
|
1083
|
-
code: "http_error",
|
|
1084
|
-
message: `${method} ${path} failed: HTTP ${res.status} ${res.statusText}`,
|
|
1085
|
-
errorType: httpStatusToErrorType(res.status),
|
|
1086
|
-
httpStatus: res.status,
|
|
1087
|
-
...requestId !== void 0 && { requestId }
|
|
1088
|
-
},
|
|
1089
|
-
exitCode
|
|
1090
|
-
);
|
|
1091
|
-
}
|
|
1092
|
-
function httpStatusToErrorType(status) {
|
|
1093
|
-
if (status === 400) return "bad_request";
|
|
1094
|
-
if (status === 401) return "unauthorized";
|
|
1095
|
-
if (status === 403) return "forbidden";
|
|
1096
|
-
if (status === 404) return "not_found";
|
|
1097
|
-
if (status === 409) return "conflict";
|
|
1098
|
-
if (status === 413) return "payload_too_large";
|
|
1099
|
-
if (status === 422) return "unprocessable_entity";
|
|
1100
|
-
if (status === 429) return "too_many_requests";
|
|
1101
|
-
return "internal_error";
|
|
1102
|
-
}
|
|
1103
|
-
async function tryParseApiError(res) {
|
|
1104
|
-
try {
|
|
1105
|
-
const json = await res.clone().json();
|
|
1106
|
-
const parsed = ApiErrorSchema.safeParse(json);
|
|
1107
|
-
return parsed.success ? parsed.data : null;
|
|
1108
|
-
} catch {
|
|
1109
|
-
return null;
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
function buildApiUrl(hostname, path, params) {
|
|
1113
|
-
if (!path.startsWith("/") || path.startsWith("//") || path.startsWith("/\\")) {
|
|
1114
|
-
throw new TiroError(
|
|
1115
|
-
{
|
|
1116
|
-
code: "internal_error",
|
|
1117
|
-
message: `API path must start with a single "/": got ${JSON.stringify(path)}`,
|
|
1118
|
-
errorType: "internal_error"
|
|
1119
|
-
},
|
|
1120
|
-
ExitCode.Generic
|
|
1121
|
-
);
|
|
1122
|
-
}
|
|
1123
|
-
const u = new URL(`${hostname}${path}`);
|
|
1124
|
-
if (params) {
|
|
1125
|
-
for (const [k, v] of Object.entries(params)) {
|
|
1126
|
-
if (v !== void 0 && v !== null && v !== "") {
|
|
1127
|
-
u.searchParams.set(k, String(v));
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
return u.toString();
|
|
1132
|
-
}
|
|
1133
|
-
function createApiClient(opts = {}) {
|
|
1134
|
-
if (opts.tokenOverride) {
|
|
1135
|
-
return new TiroApiClient(getHostname(opts.hostnameOverride), opts.tokenOverride);
|
|
1136
|
-
}
|
|
1137
|
-
const t = resolveToken();
|
|
1138
|
-
if (!t) throw authRequired();
|
|
1139
|
-
const hostname = getHostname(opts.hostnameOverride ?? t.hostname);
|
|
1140
|
-
return new TiroApiClient(hostname, t.accessToken);
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
// src/lib/util/parseDate.ts
|
|
1144
|
-
var RELATIVE_RE = /^(\d+)([smhdw])$/i;
|
|
1145
|
-
var UNIT_MS = {
|
|
1146
|
-
s: 1e3,
|
|
1147
|
-
m: 6e4,
|
|
1148
|
-
h: 36e5,
|
|
1149
|
-
d: 864e5,
|
|
1150
|
-
w: 6048e5
|
|
1151
|
-
};
|
|
1152
|
-
function parseDate(input) {
|
|
1153
|
-
const trimmed = input.trim();
|
|
1154
|
-
const rel = trimmed.match(RELATIVE_RE);
|
|
1155
|
-
if (rel) {
|
|
1156
|
-
const num = parseInt(rel[1] ?? "", 10);
|
|
1157
|
-
const unit = (rel[2] ?? "").toLowerCase();
|
|
1158
|
-
const factor = UNIT_MS[unit];
|
|
1159
|
-
if (!factor || !Number.isFinite(num)) {
|
|
1160
|
-
throw invalidDate(input);
|
|
1161
|
-
}
|
|
1162
|
-
return new Date(Date.now() - num * factor).toISOString();
|
|
1163
|
-
}
|
|
1164
|
-
const d = new Date(trimmed);
|
|
1165
|
-
if (Number.isNaN(d.getTime())) throw invalidDate(input);
|
|
1166
|
-
return d.toISOString();
|
|
1167
|
-
}
|
|
1168
|
-
function invalidDate(input) {
|
|
1169
|
-
return new TiroError(
|
|
1170
|
-
{
|
|
1171
|
-
code: "invalid_date",
|
|
1172
|
-
message: `Invalid date: "${input}". Use ISO-8601 (e.g. 2026-04-01T10:00:00Z) or relative (e.g. 7d, 24h, 30m).`,
|
|
1173
|
-
errorType: "bad_request"
|
|
1174
|
-
},
|
|
1175
|
-
ExitCode.Usage
|
|
1176
|
-
);
|
|
1177
|
-
}
|
|
1178
|
-
|
|
1179
|
-
// src/lib/util/noteFilter.ts
|
|
1180
|
-
var PLACEHOLDER_TITLE = "Untitled";
|
|
1181
|
-
var PLACEHOLDER_SOURCE_TYPES = /* @__PURE__ */ new Set(["onboarding"]);
|
|
1182
|
-
function isVisibleNote(note) {
|
|
1183
|
-
if (note.title === PLACEHOLDER_TITLE) return false;
|
|
1184
|
-
if (note.sourceType !== null && note.sourceType !== void 0 && PLACEHOLDER_SOURCE_TYPES.has(note.sourceType)) {
|
|
1185
|
-
return false;
|
|
1186
|
-
}
|
|
1187
|
-
return true;
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
// src/commands/notes/list.ts
|
|
1191
|
-
var ListResponseSchema = PageCursorResponseSchema(NoteSchema);
|
|
1192
|
-
var DEFAULT_PAGE_SIZE = 100;
|
|
1193
|
-
var MAX_PAGE_SIZE = 1e3;
|
|
1194
|
-
var HELP_AFTER = `
|
|
95
|
+
</html>`}import ZA from"open";async function Le(e){try{await ZA(e,{wait:!1})}catch{}}import{Entry as oe}from"@napi-rs/keyring";var ne="io.tiro.cli",ie="default";function Re(e){let A=new oe(ne,ie);try{A.setPassword(JSON.stringify(e))}catch(t){throw new f({code:"keychain_write_failed",message:`Failed to write to OS keychain: ${t.message}`,errorType:"internal_error",suggestion:process.platform==="linux"?"Linux requires a Secret Service daemon (gnome-keyring or kwallet). Or set TIRO_TOKEN env var.":"Check OS keychain permissions, or set TIRO_TOKEN env var."},u.Generic)}}function Ee(){let e=new oe(ne,ie),A;try{A=e.getPassword()}catch{return null}if(!A)return null;try{return JSON.parse(A)}catch{return null}}function Ie(){let e=new oe(ne,ie);try{return e.deletePassword()}catch{return!1}}function Q(){let e=process.env.TIRO_TOKEN;if(e)return{accessToken:e,source:"env"};let A=Ee();return A?{accessToken:A.accessToken,source:"keychain",hostname:A.hostname,expiresAt:A.expiresAt,...A.userId!==void 0&&{userId:A.userId}}:null}function z(e){let A=e.split(".");if(A.length!==3)return null;try{let t=A[1];if(!t)return null;let r=Buffer.from(t,"base64url").toString("utf8");return JSON.parse(r)}catch{return null}}import JA from"conf";var $A="https://api.tiro.ooo",_A=new Set(["localhost","127.0.0.1","[::1]","::1"]),x=new JA({projectName:"tiro",defaults:{hostname:$A,oauthClientId:null,oauthClientIdRegisteredAt:null,oauthClientHostname:null,oauthClientRedirectUri:null,defaultOutputDir:null}});function se(e){let A=e.trim();if(A==="")throw X("empty hostname",e);let t;try{t=new URL(A)}catch{throw X("not a valid URL",e)}if(t.protocol!=="https:"){if(!(t.protocol==="http:"&&et(t.hostname)))throw X(`disallowed scheme "${t.protocol}" \u2014 only https:// or http://localhost is permitted`,e)}if(t.username!==""||t.password!=="")throw X("URL must not embed credentials",e);if(t.search!==""||t.hash!=="")throw X("URL must not include query or fragment",e);if(t.pathname!==""&&t.pathname!=="/")throw X("URL must be host root (no path segment)",e);let r=t.origin;return tt(r)}function et(e){return e.startsWith("[")&&e.endsWith("]")&&(e=e.slice(1,-1)),_A.has(e)}function X(e,A){return new f({code:"invalid_hostname",message:`Refusing to use hostname "${A}": ${e}.`,errorType:"bad_request",suggestion:"Use https://<host> (or http://localhost for local dev). If TIRO_HOSTNAME is set in your environment, unset it."},u.Usage)}function O(e){if(e!==void 0)return se(e);let A=process.env.TIRO_HOSTNAME;return se(A||x.get("hostname"))}function Oe(e,A){let t=x.get("oauthClientId"),r=x.get("oauthClientIdRegisteredAt"),n=x.get("oauthClientHostname"),i=x.get("oauthClientRedirectUri");if(!t||!r||n!==e||!i||!At(i,A))return null;let s=696*60*60*1e3;return Date.now()-r>s?null:t}function Xe(e,A,t){x.set("oauthClientId",e),x.set("oauthClientIdRegisteredAt",Date.now()),x.set("oauthClientHostname",A),x.set("oauthClientRedirectUri",t)}function Y(){x.set("oauthClientId",null),x.set("oauthClientIdRegisteredAt",null),x.set("oauthClientHostname",null),x.set("oauthClientRedirectUri",null)}function At(e,A){let t,r;try{t=new URL(e),r=new URL(A)}catch{return!1}return t.protocol===r.protocol&&t.hostname===r.hostname&&t.pathname===r.pathname}function tt(e){return e.endsWith("/")?e.slice(0,-1):e}var rt=B.object({client_id:B.string(),client_secret:B.string().optional()}),ot=B.object({access_token:B.string(),token_type:B.string(),expires_in:B.number().optional(),scope:B.string().optional()}),nt=300*1e3,Fe="mcp:notes:read";async function Se(e={}){let A=O(e.hostname),t=e.onPrompt??(n=>process.stderr.write(`${n}
|
|
96
|
+
`)),r=await xe();try{let n=await it(A,r.redirectUri),{codeVerifier:i,codeChallenge:s}=be(),c=ye(),d=st({hostname:A,clientId:n,redirectUri:r.redirectUri,state:c,codeChallenge:s,scope:e.scope??Fe});e.noBrowser?t(`Open this URL in your browser:
|
|
97
|
+
${d}`):(t("Opening browser for sign-in..."),t(`If the browser does not open, visit:
|
|
98
|
+
${d}`),await Le(d));let p=await r.waitForCallback(nt);if(!ke(p.state,c))throw new f({code:"auth_state_mismatch",message:"OAuth state mismatch \u2014 possible CSRF. Aborting.",errorType:"unauthorized"},u.Generic);let l=await at({hostname:A,clientId:n,code:p.code,redirectUri:r.redirectUri,codeVerifier:i}),w=ct(l.expires_in),b=z(l.access_token),h=typeof b?.sub=="string"?b.sub:void 0,T={accessToken:l.access_token,tokenType:l.token_type,expiresAt:w,hostname:A,...l.scope!==void 0&&{scope:l.scope},...h!==void 0&&{userId:h}};return Re(T),{hostname:A,userId:h,expiresAt:w}}finally{r.close()}}async function it(e,A){let t=Oe(e,A);if(t)return t;let r=`${e}/v1/mcp/oauth/register`,n;try{n=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({client_name:"tiro-cli",redirect_uris:[A],grant_types:["authorization_code"],response_types:["code"],token_endpoint_auth_method:"none",scope:Fe})})}catch(c){throw new f({code:"network_error",message:`Failed to reach ${e}: ${c.message}`,errorType:"network_error",suggestion:"Check your network connection or --hostname."},u.Generic)}if(!n.ok){let c=await De(n);throw new f({code:"oauth_register_failed",message:`Dynamic Client Registration failed: HTTP ${n.status}`,errorType:"internal_error",httpStatus:n.status,...c!==""&&{suggestion:c.slice(0,200)}},u.Generic)}let i=await n.json(),s=rt.safeParse(i);if(!s.success)throw new f({code:"oauth_register_invalid",message:"Registration response did not match expected shape.",errorType:"internal_error"},u.Generic);return Xe(s.data.client_id,e,A),s.data.client_id}function st(e){let A=new URL(`${e.hostname}/v1/mcp/oauth/authorize`);return A.searchParams.set("response_type","code"),A.searchParams.set("client_id",e.clientId),A.searchParams.set("redirect_uri",e.redirectUri),A.searchParams.set("state",e.state),A.searchParams.set("code_challenge",e.codeChallenge),A.searchParams.set("code_challenge_method","S256"),A.searchParams.set("scope",e.scope),A.toString()}async function at(e){let A=`${e.hostname}/v1/mcp/oauth/token`,t=new URLSearchParams({grant_type:"authorization_code",code:e.code,redirect_uri:e.redirectUri,client_id:e.clientId,code_verifier:e.codeVerifier}),r;try{r=await fetch(A,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:t.toString()})}catch(s){throw new f({code:"network_error",message:`Failed to reach ${e.hostname}: ${s.message}`,errorType:"network_error"},u.Generic)}if(!r.ok){(r.status===400||r.status===401)&&Y();let s=await De(r);throw new f({code:"oauth_token_failed",message:`Token exchange failed: HTTP ${r.status}`,errorType:"unauthorized",httpStatus:r.status,...s!==""&&{suggestion:s.slice(0,200)}},u.AuthRequired)}let n=await r.json(),i=ot.safeParse(n);if(!i.success)throw new f({code:"oauth_token_invalid",message:"Token response did not match expected shape.",errorType:"internal_error"},u.Generic);return i.data}function ct(e){let t=e??15552e3;return Date.now()+t*1e3}async function De(e){try{return await e.text()}catch{return""}}function Pe(e){e.command("login").description("Sign in to Tiro via OAuth (browser-based, PKCE)").option("--hostname <url>","API base URL (overrides config / TIRO_HOSTNAME)").option("--no-browser","Print the URL instead of opening a browser").action(async(A,t)=>{let r=t.optsWithGlobals(),n=await Se({...A.hostname!==void 0&&{hostname:A.hostname},noBrowser:A.noBrowser===!0,onPrompt:c=>{r.quiet||process.stderr.write(`${a(c,"cyan",r)}
|
|
99
|
+
`)}}),i=O(A.hostname),s=new Date(n.expiresAt).toISOString();r.json?v({ok:!0,data:{signedIn:!0,hostname:i,userId:n.userId??null,expiresAt:s}},r):r.quiet||(process.stderr.write(`${a("\u2713","green",r)} Signed in to ${i}
|
|
100
|
+
`),n.userId&&process.stderr.write(` user: ${n.userId}
|
|
101
|
+
`),process.stderr.write(` token expires: ${s}
|
|
102
|
+
`))})}import"commander";import"zod";import{z as o}from"zod";var pt=o.object({guid:o.string(),name:o.string(),email:o.string(),role:o.enum(["OWNER","EDITOR","VIEWER"])}),dt=o.object({name:o.string().nullable().optional(),email:o.string().nullable().optional()}),L=o.object({guid:o.string(),title:o.string(),createdAt:o.string(),updatedAt:o.string(),sourceType:o.string(),recordingDurationSeconds:o.number(),collaborators:o.array(pt).optional().default([]),participants:o.array(dt).optional().default([]),webUrl:o.string(),recordingStartAt:o.string().nullable().optional(),recordingEndAt:o.string().nullable().optional()}).passthrough(),lt=o.object({type:o.string(),content:o.string()}),ut=o.object({label:o.string(),personName:o.string().nullable().optional()}),mt=o.object({content:o.string(),speaker:ut}),F=o.object({uuid:o.string(),transcribeLocale:o.string().nullable().optional(),transcript:lt.nullable().optional(),diarizedSegments:o.array(mt).nullable().optional(),timeFrom:o.string().nullable().optional(),timeTo:o.string().nullable().optional(),locked:o.boolean().optional()}).passthrough(),gt=o.object({content:o.string(),speaker:o.object({label:o.string(),name:o.string().nullable()}).nullable()}),ft=o.object({timeFrom:o.string().nullable(),timeTo:o.string().nullable(),segments:o.array(gt)}),tn=o.object({noteGuid:o.string(),title:o.string(),participants:o.array(o.string()),createdAt:o.string(),recordingDurationSeconds:o.number(),paragraphs:o.array(ft)}),R=e=>o.object({content:o.array(e),nextCursor:o.string().nullable()}),S=e=>o.object({content:o.array(e)}),qe=o.object({error:o.object({code:o.number(),errorType:o.string(),message:o.string(),detail:o.string().nullable().optional()})}),He=o.object({guid:o.string()}).passthrough(),ht=o.object({guid:o.string(),name:o.string(),isWikiEnabled:o.boolean()}).passthrough(),We=o.object({workspaces:o.array(ht)}).passthrough(),Me=o.object({workspaceGuid:o.string().nullable(),userId:o.number().nullable().optional(),apiKeyName:o.string().nullable().optional()}).passthrough(),wt=o.object({guid:o.string(),wikiId:o.number(),canonicalName:o.string(),pageType:o.string(),entitySubtype:o.string().nullable().optional(),score:o.number()}).passthrough(),Ne=o.object({items:o.array(wt)}).passthrough(),Ge=o.object({guid:o.string(),noteId:o.number(),paragraphId:o.number().nullable().optional(),paragraphUuid:o.string().nullable().optional(),kind:o.string(),sourceUserId:o.number(),extractedText:o.string(),confidence:o.number().nullable().optional(),createdAt:o.string()}).passthrough(),bt=o.object({guid:o.string(),alias:o.string(),source:o.string(),sourceMentionGuid:o.string().nullable().optional(),sourceUserId:o.number().nullable().optional(),createdAt:o.string()}).passthrough(),yt=o.object({guid:o.string(),sourcePageGuid:o.string(),sourcePageName:o.string().nullable().optional(),targetPageGuid:o.string(),targetPageName:o.string().nullable().optional(),linkType:o.string(),linkTypeDisplayKo:o.string().optional(),linkTypeDisplayEn:o.string().optional(),isDirectional:o.boolean(),source:o.string().optional(),creatorUserId:o.number().nullable().optional(),createdAt:o.string().optional(),updatedAt:o.string().optional()}).passthrough(),Ve=o.object({guid:o.string(),wikiId:o.number(),canonicalName:o.string(),description:o.string().nullable().optional(),descriptionStatus:o.string().nullable().optional(),regenerationAvailable:o.boolean().nullable().optional(),pageType:o.string(),entitySubtype:o.string().nullable().optional(),extractionStatus:o.string(),mentionCountVisible:o.number(),mentions:o.array(Ge),aliases:o.array(bt),links:o.array(yt),lastUpdatedVisible:o.string().nullable().optional(),createdAt:o.string(),updatedAt:o.string()}).passthrough(),Ue=o.object({items:o.array(Ge),nextCursorCreatedAt:o.string().nullable(),nextCursorId:o.number().nullable()}).passthrough(),kt=o.object({guid:o.string(),canonicalName:o.string(),pageType:o.string(),entitySubtype:o.string().nullable().optional(),extractionStatus:o.string(),mentionCountVisible:o.number()}).passthrough(),Ct=o.object({guid:o.string(),sourcePageGuid:o.string(),targetPageGuid:o.string(),linkType:o.string(),linkTypeDisplayKo:o.string().optional(),linkTypeDisplayEn:o.string().optional(),isDirectional:o.boolean()}).passthrough(),M=o.object({nodes:o.array(kt),edges:o.array(Ct)}).passthrough(),je=o.object({error_code:o.enum(["WIKI_PLAN_REQUIRED","WIKI_NOT_ACTIVATED"]),message:o.string(),current_plan:o.string().nullable().optional(),required_plans:o.array(o.string()).nullable().optional(),action_url:o.string().nullable().optional()}).passthrough(),Qe=o.object({id:o.string(),workspaceGuid:o.string(),title:o.string(),description:o.string(),color:o.string(),sharingType:o.string(),parentId:o.string().nullable().optional(),isTeamFolder:o.boolean(),createdAt:o.string(),updatedAt:o.string()}).passthrough(),ae=o.lazy(()=>o.object({id:o.string(),title:o.string(),parentId:o.string().nullable().optional(),depth:o.number(),isAccessible:o.boolean(),children:o.array(ae)})),ze=o.object({id:o.number(),entry:o.string(),createdAt:o.string()}).passthrough();var K=class{constructor(A,t){this.hostname=A;this.token=t}hostname;token;async getJson(A,t,r){let n=this.buildUrl(A,r),i=await this.fetch(n,{method:"GET"});return this.parseJson(i,t,"GET",A)}async postJson(A,t,r){let n=this.buildUrl(A),i=await this.fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)});return this.parseJson(i,t,"POST",A)}async putJson(A,t,r){let n=this.buildUrl(A),i=await this.fetch(n,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)});return this.parseJson(i,t,"PUT",A)}async deleteVoid(A){let t=this.buildUrl(A),r=await this.fetch(t,{method:"DELETE"});if(!r.ok)throw await Ye(r,"DELETE",A)}buildUrl(A,t){return Lt(this.hostname,A,t)}async fetch(A,t){let r=new Headers(t.headers);r.set("Authorization",`Bearer ${this.token}`),r.set("Accept","application/json");try{return await fetch(A,{...t,headers:r})}catch(n){throw new f({code:"network_error",message:`Network error reaching ${this.hostname}: ${n.message}`,errorType:"network_error",suggestion:"Check your network or --hostname."},u.Generic)}}async parseJson(A,t,r,n){if(!A.ok)throw await Ye(A,r,n);let i;try{i=await A.json()}catch(c){throw new f({code:"invalid_response",message:`Failed to parse JSON from ${r} ${n}: ${c.message}`,errorType:"internal_error"},u.Generic)}let s=t.safeParse(i);if(!s.success)throw new f({code:"schema_mismatch",message:`Response shape did not match expected schema (${r} ${n}).`,errorType:"internal_error",suggestion:s.error.issues.slice(0,3).map(c=>`${c.path.join(".")||"(root)"}: ${c.message}`).join("; ")},u.Generic);return s.data}};async function Ye(e,A,t){let r=e.headers.get("x-request-id")??void 0,n=e.status===401?u.AuthRequired:u.Generic;if(e.status===402){let s=await xt(e);if(s)return Bt(s,r)}let i=await Tt(e);return i?new f({code:`${i.error.errorType}`,message:i.error.message,errorType:i.error.errorType,httpStatus:e.status,...r!==void 0&&{requestId:r},...e.status===401&&{suggestion:"Run `tiro auth login` to refresh."}},n):new f({code:"http_error",message:`${A} ${t} failed: HTTP ${e.status} ${e.statusText}`,errorType:vt(e.status),httpStatus:e.status,...r!==void 0&&{requestId:r}},n)}function vt(e){return e===400?"bad_request":e===401?"unauthorized":e===402?"payment_required":e===403?"forbidden":e===404?"not_found":e===409?"conflict":e===413?"payload_too_large":e===422?"unprocessable_entity":e===429?"too_many_requests":"internal_error"}async function Tt(e){try{let A=await e.clone().json(),t=qe.safeParse(A);return t.success?t.data:null}catch{return null}}async function xt(e){try{let A=await e.clone().json(),t=je.safeParse(A);return t.success?t.data:null}catch{return null}}function Bt(e,A){let t=e.action_url??null;return new f({code:e.error_code,message:e.message,errorType:"payment_required",httpStatus:402,...t?{suggestion:`Upgrade: ${t}`}:{},...A!==void 0&&{requestId:A},details:{...e}},u.Generic)}function Lt(e,A,t){if(!A.startsWith("/")||A.startsWith("//")||A.startsWith("/\\"))throw new f({code:"internal_error",message:`API path must start with a single "/": got ${JSON.stringify(A)}`,errorType:"internal_error"},u.Generic);let r=new URL(`${e}${A}`);if(t)for(let[n,i]of Object.entries(t))i!=null&&i!==""&&r.searchParams.set(n,String(i));return r.toString()}function k(e={}){if(e.tokenOverride)return new K(O(e.hostnameOverride),e.tokenOverride);let A=Q();if(!A)throw W();let t=O(e.hostnameOverride??A.hostname);return new K(t,A.accessToken)}var Ke=new WeakMap;async function E(e){let A=Ke.get(e);if(A)return A;let t=e.getJson("/v1/external/workspaces/me",He).then(r=>r.guid);return Ke.set(e,t),t}async function Ze(e){return(await e.getJson("/v1/external/workspaces",We)).workspaces}async function ce(e){return e.getJson("/v1/external/auth/me",Me)}async function N(e,A){return A||(await ce(e)).workspaceGuid}var Z="Credential is not bound to a workspace; listing across all your workspaces. Pass --workspace <guid> (see `tiro wiki workspaces`) or use a workspace-scoped API key to target one.";async function D(e,A){let t=await N(e,A);if(!t)throw new f({code:"workspace_required",message:"This command targets a single workspace, but your credential is not bound to one.",errorType:"bad_request",suggestion:"Pass --workspace <guid> (run `tiro wiki workspaces` for guids) or use a workspace-scoped API key."},u.Usage);return t}function Je(e){e.command("status").description("Show current authenticated account and scopes").action(async(A,t)=>{let r=t.optsWithGlobals(),n=Q();if(!n)throw W();let i=z(n.accessToken),s=typeof i?.sub=="string"?i.sub:null,c=typeof i?.exp=="number"?i.exp*1e3:null,d=typeof i?.scope=="string"?i.scope:null,p=c??n.expiresAt??null,l=p!==null&&Date.now()>=p,w=null,b=!1;if(!l)try{let T=k({tokenOverride:n.accessToken,hostnameOverride:r.hostname??n.hostname??void 0});w=(await ce(T)).workspaceGuid,b=!0}catch{}let h={signedIn:!0,source:n.source,hostname:n.hostname??null,userId:n.userId??s,scope:d,...b&&{workspace:w},expiresAt:p?new Date(p).toISOString():null,expired:l,tokenPrefix:`${n.accessToken.slice(0,4)}...***`};if(r.json||!process.stdout.isTTY)v({ok:!0,data:h},r);else{let T=l?a("!","yellow",r):a("\u2713","green",r),H=l?"Token expired":"Signed in";if(process.stdout.write(`${T} ${H}
|
|
103
|
+
`),process.stdout.write(` source: ${h.source}
|
|
104
|
+
`),h.hostname&&process.stdout.write(` hostname: ${h.hostname}
|
|
105
|
+
`),h.userId&&process.stdout.write(` user: ${h.userId}
|
|
106
|
+
`),h.scope&&process.stdout.write(` scope: ${h.scope}
|
|
107
|
+
`),b?process.stdout.write(` workspace: ${w??a("(unbound \u2014 across all workspaces)","gray",r)}
|
|
108
|
+
`):l||process.stdout.write(` workspace: ${a("(unable to verify)","yellow",r)}
|
|
109
|
+
`),h.expiresAt){let Ae=l?a(" (expired)","red",r):"";process.stdout.write(` expires at: ${h.expiresAt}${Ae}
|
|
110
|
+
`)}process.stdout.write(` token: ${h.tokenPrefix}
|
|
111
|
+
`),l&&process.stdout.write(`
|
|
112
|
+
${a("\u2192","gray",r)} Run \`tiro auth login\` to refresh.
|
|
113
|
+
`)}})}import"commander";function $e(e){e.command("logout").description("Sign out and clear the stored token").action(async(A,t)=>{let r=t.optsWithGlobals(),n=Ie();Y(),r.json?v({ok:!0,data:{signedOut:!0,hadToken:n}},r):r.quiet||(n?process.stderr.write(`${a("\u2713","green",r)} Signed out
|
|
114
|
+
`):process.stderr.write(`${a("\u2022","gray",r)} No token was stored
|
|
115
|
+
`))})}function _e(e){let A=e.command("auth").description("Manage authentication");Pe(A),Je(A),$e(A)}import"commander";import"commander";var Rt=/^(\d+)([smhdw])$/i,Et={s:1e3,m:6e4,h:36e5,d:864e5,w:6048e5};function I(e){let A=e.trim(),t=A.match(Rt);if(t){let n=parseInt(t[1]??"",10),i=(t[2]??"").toLowerCase(),s=Et[i];if(!s||!Number.isFinite(n))throw eA(e);return new Date(Date.now()-n*s).toISOString()}let r=new Date(A);if(Number.isNaN(r.getTime()))throw eA(e);return r.toISOString()}function eA(e){return new f({code:"invalid_date",message:`Invalid date: "${e}". Use ISO-8601 (e.g. 2026-04-01T10:00:00Z) or relative (e.g. 7d, 24h, 30m).`,errorType:"bad_request"},u.Usage)}var It="Untitled",Ot=new Set(["onboarding"]);function J(e){return!(e.title===It||e.sourceType!==null&&e.sourceType!==void 0&&Ot.has(e.sourceType))}var Xt=R(L),AA=100,tA=200,Ft=`
|
|
1195
116
|
Examples:
|
|
1196
117
|
tiro notes list --since 7d
|
|
1197
118
|
tiro notes list --keyword "OKR" --since 30d --json
|
|
@@ -1207,101 +128,12 @@ Keyword matching:
|
|
|
1207
128
|
Note: placeholder notes (title='Untitled' or sourceType='onboarding') are
|
|
1208
129
|
filtered out by default. A page of N may return fewer than N visible notes \u2014
|
|
1209
130
|
keep paginating to fetch more, or pass --include-untitled to surface them.
|
|
1210
|
-
`;
|
|
1211
|
-
function
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
"--limit <n>",
|
|
1217
|
-
`Max results per page (default ${DEFAULT_PAGE_SIZE}, max ${MAX_PAGE_SIZE})`
|
|
1218
|
-
).option("--cursor <token>", "Continue a previous page").option(
|
|
1219
|
-
"--include-untitled",
|
|
1220
|
-
"Include placeholder notes (title='Untitled' or sourceType='onboarding'). Default: hidden",
|
|
1221
|
-
false
|
|
1222
|
-
).addHelpText("after", HELP_AFTER).action(async (opts, cmd) => {
|
|
1223
|
-
const globalOpts = cmd.optsWithGlobals();
|
|
1224
|
-
const client = createApiClient({
|
|
1225
|
-
...globalOpts.hostname !== void 0 && { hostnameOverride: globalOpts.hostname }
|
|
1226
|
-
});
|
|
1227
|
-
const params = {};
|
|
1228
|
-
if (opts.keyword) params["keyword"] = opts.keyword;
|
|
1229
|
-
if (opts.folder) params["folderId"] = opts.folder;
|
|
1230
|
-
if (opts.since) params["createdAtFrom"] = parseDate(opts.since);
|
|
1231
|
-
if (opts.until) params["createdAtTo"] = parseDate(opts.until);
|
|
1232
|
-
const size = clampLimit(opts.limit);
|
|
1233
|
-
if (size !== void 0) params["size"] = size;
|
|
1234
|
-
if (opts.cursor) params["cursor"] = opts.cursor;
|
|
1235
|
-
const res = await client.getJson("/v1/external/notes", ListResponseSchema, params);
|
|
1236
|
-
const visible = opts.includeUntitled === true ? res.content : res.content.filter(isVisibleNote);
|
|
1237
|
-
const mode = resolveOutputMode(globalOpts);
|
|
1238
|
-
if (mode === "json") {
|
|
1239
|
-
for (const note of visible) printNdjson(note);
|
|
1240
|
-
if (res.nextCursor) printNdjson({ _cursor: res.nextCursor });
|
|
1241
|
-
} else {
|
|
1242
|
-
printPretty(visible, res.nextCursor, globalOpts);
|
|
1243
|
-
}
|
|
1244
|
-
});
|
|
1245
|
-
}
|
|
1246
|
-
function clampLimit(raw) {
|
|
1247
|
-
if (!raw) return void 0;
|
|
1248
|
-
const n = parseInt(raw, 10);
|
|
1249
|
-
if (!Number.isFinite(n) || n <= 0) return DEFAULT_PAGE_SIZE;
|
|
1250
|
-
return Math.min(n, MAX_PAGE_SIZE);
|
|
1251
|
-
}
|
|
1252
|
-
function printPretty(notes, nextCursor, opts) {
|
|
1253
|
-
if (notes.length === 0) {
|
|
1254
|
-
process.stdout.write(`${color("(no notes)", "gray", opts)}
|
|
1255
|
-
`);
|
|
1256
|
-
return;
|
|
1257
|
-
}
|
|
1258
|
-
const titleWidth = computeTitleWidth();
|
|
1259
|
-
for (const n of notes) {
|
|
1260
|
-
const date = n.createdAt.slice(0, 10);
|
|
1261
|
-
const dur = formatDuration(n.recordingDurationSeconds);
|
|
1262
|
-
const title = truncate(n.title, titleWidth);
|
|
1263
|
-
process.stdout.write(
|
|
1264
|
-
`${color(date, "gray", opts)} ${color(n.guid, "dim", opts)} ${color(dur, "cyan", opts)} ${title}
|
|
1265
|
-
`
|
|
1266
|
-
);
|
|
1267
|
-
}
|
|
1268
|
-
if (nextCursor) {
|
|
1269
|
-
process.stdout.write(
|
|
1270
|
-
`${color(`
|
|
1271
|
-
next: --cursor ${nextCursor}`, "gray", opts)}
|
|
1272
|
-
`
|
|
1273
|
-
);
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
function computeTitleWidth() {
|
|
1277
|
-
const cols = process.stdout.columns;
|
|
1278
|
-
if (!cols || cols < 60) return 40;
|
|
1279
|
-
return Math.max(20, cols - 60);
|
|
1280
|
-
}
|
|
1281
|
-
function truncate(s, max) {
|
|
1282
|
-
if (s.length <= max) return s;
|
|
1283
|
-
return s.slice(0, Math.max(0, max - 1)) + "\u2026";
|
|
1284
|
-
}
|
|
1285
|
-
function formatDuration(sec) {
|
|
1286
|
-
if (!sec || sec <= 0) return "\u2014";
|
|
1287
|
-
const m = Math.floor(sec / 60);
|
|
1288
|
-
const s = Math.floor(sec % 60);
|
|
1289
|
-
if (m > 0) return `${m}m${s.toString().padStart(2, "0")}s`;
|
|
1290
|
-
return `${s}s`;
|
|
1291
|
-
}
|
|
1292
|
-
|
|
1293
|
-
// src/commands/notes/search.ts
|
|
1294
|
-
import "commander";
|
|
1295
|
-
import { z as z4 } from "zod";
|
|
1296
|
-
var SearchResponseSchema = z4.object({
|
|
1297
|
-
notes: z4.array(NoteSchema),
|
|
1298
|
-
nextCursor: z4.string().nullable(),
|
|
1299
|
-
degraded: z4.boolean().optional(),
|
|
1300
|
-
degradedReason: z4.string().nullable().optional()
|
|
1301
|
-
}).passthrough();
|
|
1302
|
-
var DEFAULT_PAGE_SIZE2 = 100;
|
|
1303
|
-
var MAX_PAGE_SIZE2 = 1e3;
|
|
1304
|
-
var HELP_AFTER2 = `
|
|
131
|
+
`;function rA(e){e.command("list").description("List notes (lightweight metadata).").option("--keyword <text>",'Reorder by full-text relevance for this keyword (e.g. "OKR")').option("--folder <id>","Restrict to a folder and its descendants").option("--since <date>","Inclusive lower bound on createdAt (ISO-8601 or relative: 7d, 24h, 30m)").option("--until <date>","Exclusive upper bound on createdAt").option("--limit <n>",`Max results per page (default ${AA}, max ${tA})`).option("--cursor <token>","Continue a previous page").option("--workspace <guid>","List within one workspace (see `tiro wiki workspaces`). Default: the workspace your API key is bound to, else across all your workspaces").option("--include-untitled","Include placeholder notes (title='Untitled' or sourceType='onboarding'). Default: hidden",!1).addHelpText("after",Ft).action(async(A,t)=>{let r=t.optsWithGlobals(),n=k({...r.hostname!==void 0&&{hostnameOverride:r.hostname}}),i={};A.keyword&&(i.keyword=A.keyword),A.folder&&(i.folderId=A.folder),A.since&&(i.createdAtFrom=I(A.since)),A.until&&(i.createdAtTo=I(A.until));let s=St(A.limit);s!==void 0&&(i.size=s),A.cursor&&(i.cursor=A.cursor);let c=await N(n,A.workspace);!c&&r.quiet!==!0&&process.stderr.write(`${a("\u26A0","yellow",r)} ${Z}
|
|
132
|
+
`);let d=c?`/v1/external/workspaces/${encodeURIComponent(c)}/notes`:"/v1/external/notes",p=await n.getJson(d,Xt,i),l=A.includeUntitled===!0?p.content:p.content.filter(J);if(y(r)==="json"){for(let b of l)C(b);p.nextCursor&&C({_cursor:p.nextCursor})}else Dt(l,p.nextCursor,r)})}function St(e){if(!e)return;let A=parseInt(e,10);return!Number.isFinite(A)||A<=0?AA:Math.min(A,tA)}function Dt(e,A,t){if(e.length===0){process.stdout.write(`${a("(no notes)","gray",t)}
|
|
133
|
+
`);return}let r=Pt();for(let n of e){let i=n.createdAt.slice(0,10),s=Ht(n.recordingDurationSeconds),c=qt(n.title,r);process.stdout.write(`${a(i,"gray",t)} ${a(n.guid,"dim",t)} ${a(s,"cyan",t)} ${c}
|
|
134
|
+
`)}A&&process.stdout.write(`${a(`
|
|
135
|
+
next: --cursor ${A}`,"gray",t)}
|
|
136
|
+
`)}function Pt(){let e=process.stdout.columns;return!e||e<60?40:Math.max(20,e-60)}function qt(e,A){return e.length<=A?e:e.slice(0,Math.max(0,A-1))+"\u2026"}function Ht(e){if(!e||e<=0)return"\u2014";let A=Math.floor(e/60),t=Math.floor(e%60);return A>0?`${A}m${t.toString().padStart(2,"0")}s`:`${t}s`}import"commander";import{z as G}from"zod";var Wt=G.object({notes:G.array(L),nextCursor:G.string().nullable(),degraded:G.boolean().optional(),degradedReason:G.string().nullable().optional()}).passthrough(),oA=100,nA=200,Mt=`
|
|
1305
137
|
Examples:
|
|
1306
138
|
tiro notes search "Q3 Planning"
|
|
1307
139
|
tiro notes search "Acme Corp" --since 7d --json
|
|
@@ -1321,344 +153,25 @@ Ordering:
|
|
|
1321
153
|
|
|
1322
154
|
Note: placeholder notes (title='Untitled' or sourceType='onboarding') are
|
|
1323
155
|
filtered out by default. Pass --include-untitled to surface them.
|
|
1324
|
-
`;
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
const globalOpts = cmd.optsWithGlobals();
|
|
1344
|
-
const keyword = (positional ?? opts.keyword ?? "").trim();
|
|
1345
|
-
if (!keyword) {
|
|
1346
|
-
throw new TiroError(
|
|
1347
|
-
{
|
|
1348
|
-
code: "missing_keyword",
|
|
1349
|
-
message: "search requires a keyword (positional or --keyword).",
|
|
1350
|
-
errorType: "bad_request",
|
|
1351
|
-
suggestion: 'tiro notes search "OKR"'
|
|
1352
|
-
},
|
|
1353
|
-
ExitCode.Usage
|
|
1354
|
-
);
|
|
1355
|
-
}
|
|
1356
|
-
const filter = {};
|
|
1357
|
-
if (opts.folder) filter["folderId"] = opts.folder;
|
|
1358
|
-
if (opts.since) filter["createdAtFrom"] = parseDate(opts.since);
|
|
1359
|
-
if (opts.until) filter["createdAtTo"] = parseDate(opts.until);
|
|
1360
|
-
const pagination = {};
|
|
1361
|
-
const size = clampLimit2(opts.limit);
|
|
1362
|
-
if (size !== void 0) pagination["size"] = size;
|
|
1363
|
-
if (opts.cursor) pagination["cursor"] = opts.cursor;
|
|
1364
|
-
const body = { keyword };
|
|
1365
|
-
if (Object.keys(filter).length > 0) body["filter"] = filter;
|
|
1366
|
-
if (Object.keys(pagination).length > 0) body["pagination"] = pagination;
|
|
1367
|
-
const client = createApiClient({
|
|
1368
|
-
...globalOpts.hostname !== void 0 && { hostnameOverride: globalOpts.hostname }
|
|
1369
|
-
});
|
|
1370
|
-
const res = await client.postJson(
|
|
1371
|
-
"/v1/external/notes/search",
|
|
1372
|
-
SearchResponseSchema,
|
|
1373
|
-
body
|
|
1374
|
-
);
|
|
1375
|
-
const visible = opts.includeUntitled === true ? res.notes : res.notes.filter(isVisibleNote);
|
|
1376
|
-
const mode = resolveOutputMode(globalOpts);
|
|
1377
|
-
if (mode === "json") {
|
|
1378
|
-
for (const note of visible) printNdjson(note);
|
|
1379
|
-
if (res.nextCursor) printNdjson({ _cursor: res.nextCursor });
|
|
1380
|
-
if (res.degraded === true) {
|
|
1381
|
-
printNdjson({ _degraded: true, _degradedReason: res.degradedReason ?? null });
|
|
1382
|
-
}
|
|
1383
|
-
} else {
|
|
1384
|
-
printPretty2(visible, res.nextCursor, globalOpts);
|
|
1385
|
-
if (res.degraded === true && globalOpts.quiet !== true) {
|
|
1386
|
-
process.stderr.write(
|
|
1387
|
-
`${color("\u26A0", "yellow", globalOpts)} search results are partial${res.degradedReason ? ` (${res.degradedReason})` : ""}
|
|
1388
|
-
`
|
|
1389
|
-
);
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
});
|
|
1393
|
-
}
|
|
1394
|
-
function clampLimit2(raw) {
|
|
1395
|
-
if (!raw) return void 0;
|
|
1396
|
-
const n = parseInt(raw, 10);
|
|
1397
|
-
if (!Number.isFinite(n) || n <= 0) return DEFAULT_PAGE_SIZE2;
|
|
1398
|
-
return Math.min(n, MAX_PAGE_SIZE2);
|
|
1399
|
-
}
|
|
1400
|
-
function printPretty2(notes, nextCursor, opts) {
|
|
1401
|
-
if (notes.length === 0) {
|
|
1402
|
-
process.stdout.write(`${color("(no matches)", "gray", opts)}
|
|
1403
|
-
`);
|
|
1404
|
-
return;
|
|
1405
|
-
}
|
|
1406
|
-
for (const n of notes) {
|
|
1407
|
-
const date = n.createdAt.slice(0, 10);
|
|
1408
|
-
process.stdout.write(
|
|
1409
|
-
`${color(date, "gray", opts)} ${color(n.guid, "dim", opts)} ${n.title}
|
|
1410
|
-
`
|
|
1411
|
-
);
|
|
1412
|
-
}
|
|
1413
|
-
if (nextCursor) {
|
|
1414
|
-
process.stdout.write(
|
|
1415
|
-
`${color(`
|
|
1416
|
-
next: --cursor ${nextCursor}`, "gray", opts)}
|
|
1417
|
-
`
|
|
1418
|
-
);
|
|
1419
|
-
}
|
|
1420
|
-
}
|
|
1421
|
-
|
|
1422
|
-
// src/commands/notes/get.ts
|
|
1423
|
-
import "commander";
|
|
1424
|
-
|
|
1425
|
-
// src/lib/output/file.ts
|
|
1426
|
-
import { mkdir, rename, stat, writeFile, access } from "fs/promises";
|
|
1427
|
-
import { dirname as dirname2, resolve as resolve2, sep } from "path";
|
|
1428
|
-
function assertSafeOutputPath(input) {
|
|
1429
|
-
if (input.trim() === "") {
|
|
1430
|
-
throw outputError("output path is empty", input);
|
|
1431
|
-
}
|
|
1432
|
-
const segments = input.split(/[\\/]/);
|
|
1433
|
-
for (const seg of segments) {
|
|
1434
|
-
if (seg === "..") {
|
|
1435
|
-
throw outputError("path traversal segment '..' is not allowed", input);
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
}
|
|
1439
|
-
function outputError(reason, input) {
|
|
1440
|
-
return new TiroError(
|
|
1441
|
-
{
|
|
1442
|
-
code: "unsafe_output_path",
|
|
1443
|
-
message: `Refusing to write to ${JSON.stringify(input)}: ${reason}.`,
|
|
1444
|
-
errorType: "bad_request",
|
|
1445
|
-
suggestion: "Use a path without '..' segments, or pass --force if you really mean it."
|
|
1446
|
-
},
|
|
1447
|
-
ExitCode.Usage
|
|
1448
|
-
);
|
|
1449
|
-
}
|
|
1450
|
-
async function writeFileAtomic(filepath, content, opts = {}) {
|
|
1451
|
-
if (!opts.force) {
|
|
1452
|
-
assertSafeOutputPath(filepath);
|
|
1453
|
-
}
|
|
1454
|
-
const absPath = resolve2(filepath);
|
|
1455
|
-
await mkdir(dirname2(absPath), { recursive: true });
|
|
1456
|
-
if (!opts.force) {
|
|
1457
|
-
const exists = await fileExists(absPath);
|
|
1458
|
-
if (exists) {
|
|
1459
|
-
throw new TiroError(
|
|
1460
|
-
{
|
|
1461
|
-
code: "file_exists",
|
|
1462
|
-
message: `File already exists: ${absPath}`,
|
|
1463
|
-
errorType: "conflict",
|
|
1464
|
-
suggestion: "Use --force to overwrite, or pick a different --output."
|
|
1465
|
-
},
|
|
1466
|
-
ExitCode.Generic
|
|
1467
|
-
);
|
|
1468
|
-
}
|
|
1469
|
-
}
|
|
1470
|
-
const tmp = `${absPath}.tmp.${process.pid}.${Date.now()}`;
|
|
1471
|
-
await writeFile(tmp, content, "utf8");
|
|
1472
|
-
await rename(tmp, absPath);
|
|
1473
|
-
const s = await stat(absPath);
|
|
1474
|
-
return { path: absPath, size: s.size };
|
|
1475
|
-
}
|
|
1476
|
-
async function fileExists(p) {
|
|
1477
|
-
try {
|
|
1478
|
-
await access(p);
|
|
1479
|
-
return true;
|
|
1480
|
-
} catch {
|
|
1481
|
-
return false;
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
|
-
// src/lib/output/transcript.ts
|
|
1486
|
-
function buildMcpTranscript(note, paragraphs) {
|
|
1487
|
-
return {
|
|
1488
|
-
noteGuid: note.guid,
|
|
1489
|
-
title: note.title,
|
|
1490
|
-
participants: note.participants?.map((p) => p.name || p.email || "").filter((s) => typeof s === "string" && s.length > 0) ?? [],
|
|
1491
|
-
createdAt: note.createdAt,
|
|
1492
|
-
recordingDurationSeconds: note.recordingDurationSeconds,
|
|
1493
|
-
paragraphs: paragraphsToMcp(paragraphs)
|
|
1494
|
-
};
|
|
1495
|
-
}
|
|
1496
|
-
function paragraphsToMcp(paragraphs) {
|
|
1497
|
-
return paragraphs.map((p) => ({
|
|
1498
|
-
timeFrom: p.timeFrom ?? null,
|
|
1499
|
-
timeTo: p.timeTo ?? null,
|
|
1500
|
-
segments: paragraphToSegments(p)
|
|
1501
|
-
})).filter((p) => p.segments.length > 0);
|
|
1502
|
-
}
|
|
1503
|
-
function paragraphToSegments(p) {
|
|
1504
|
-
const ds = p.diarizedSegments;
|
|
1505
|
-
if (ds && ds.length > 0) {
|
|
1506
|
-
return ds.map((s) => ({
|
|
1507
|
-
content: stripHtml(s.content),
|
|
1508
|
-
speaker: {
|
|
1509
|
-
label: s.speaker.label,
|
|
1510
|
-
name: s.speaker.personName ? stripHtml(s.speaker.personName) : null
|
|
1511
|
-
}
|
|
1512
|
-
})).filter((s) => s.content.length > 0);
|
|
1513
|
-
}
|
|
1514
|
-
const plain = stripHtml(p.transcript?.content ?? "");
|
|
1515
|
-
return plain ? [{ content: plain, speaker: null }] : [];
|
|
1516
|
-
}
|
|
1517
|
-
function renderTranscriptJson(t) {
|
|
1518
|
-
return `${JSON.stringify(t, null, 2)}
|
|
1519
|
-
`;
|
|
1520
|
-
}
|
|
1521
|
-
function renderTranscriptMarkdown(t, opts = {}) {
|
|
1522
|
-
const showTs = opts.timestamps !== false;
|
|
1523
|
-
const anchor = showTs ? anchorTime(t) : null;
|
|
1524
|
-
const lines = [];
|
|
1525
|
-
lines.push(`# ${t.title}`, "");
|
|
1526
|
-
if (t.participants.length > 0) {
|
|
1527
|
-
lines.push(`**Participants**: ${t.participants.join(", ")}`, "");
|
|
1528
|
-
}
|
|
1529
|
-
lines.push("## Transcript", "");
|
|
1530
|
-
for (const p of t.paragraphs) {
|
|
1531
|
-
if (showTs) {
|
|
1532
|
-
const ts = elapsed(p.timeFrom, anchor);
|
|
1533
|
-
if (ts) lines.push(`### ${ts}`, "");
|
|
1534
|
-
}
|
|
1535
|
-
for (const s of p.segments) {
|
|
1536
|
-
const who = s.speaker?.name ?? s.speaker?.label ?? "Unknown";
|
|
1537
|
-
lines.push(`**${who}**: ${s.content}`);
|
|
1538
|
-
}
|
|
1539
|
-
lines.push("");
|
|
1540
|
-
}
|
|
1541
|
-
return `${lines.join("\n").trimEnd()}
|
|
1542
|
-
`;
|
|
1543
|
-
}
|
|
1544
|
-
function renderTranscriptText(t) {
|
|
1545
|
-
const lines = [];
|
|
1546
|
-
for (const p of t.paragraphs) {
|
|
1547
|
-
for (const s of p.segments) {
|
|
1548
|
-
const who = s.speaker?.name ?? s.speaker?.label ?? "Unknown";
|
|
1549
|
-
lines.push(`[${who}] ${s.content}`);
|
|
1550
|
-
}
|
|
1551
|
-
}
|
|
1552
|
-
return `${lines.join("\n")}
|
|
1553
|
-
`;
|
|
1554
|
-
}
|
|
1555
|
-
function anchorTime(t) {
|
|
1556
|
-
for (const p of t.paragraphs) {
|
|
1557
|
-
if (p.timeFrom) return p.timeFrom;
|
|
1558
|
-
}
|
|
1559
|
-
return null;
|
|
1560
|
-
}
|
|
1561
|
-
function elapsed(currentIso, anchorIso) {
|
|
1562
|
-
if (!currentIso || !anchorIso) return "";
|
|
1563
|
-
const cur = Date.parse(currentIso);
|
|
1564
|
-
const anc = Date.parse(anchorIso);
|
|
1565
|
-
if (!Number.isFinite(cur) || !Number.isFinite(anc)) return "";
|
|
1566
|
-
const seconds = Math.max(0, Math.floor((cur - anc) / 1e3));
|
|
1567
|
-
const h = Math.floor(seconds / 3600);
|
|
1568
|
-
const m = Math.floor(seconds % 3600 / 60);
|
|
1569
|
-
const s = seconds % 60;
|
|
1570
|
-
if (h > 0) return `${pad(h)}:${pad(m)}:${pad(s)}`;
|
|
1571
|
-
return `${pad(m)}:${pad(s)}`;
|
|
1572
|
-
}
|
|
1573
|
-
function pad(n) {
|
|
1574
|
-
return n.toString().padStart(2, "0");
|
|
1575
|
-
}
|
|
1576
|
-
function stripHtml(s) {
|
|
1577
|
-
return s.replace(/<[^>]*>/g, "").replace(/ /g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").trim();
|
|
1578
|
-
}
|
|
1579
|
-
|
|
1580
|
-
// src/lib/output/format.ts
|
|
1581
|
-
function formatNote(note, format, opts = {}) {
|
|
1582
|
-
switch (format) {
|
|
1583
|
-
case "md":
|
|
1584
|
-
return formatMarkdown(note, opts);
|
|
1585
|
-
case "json":
|
|
1586
|
-
return formatJson(note, opts);
|
|
1587
|
-
case "txt":
|
|
1588
|
-
return formatText(note, opts);
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
function formatMarkdown(note, opts) {
|
|
1592
|
-
const fm = [
|
|
1593
|
-
"---",
|
|
1594
|
-
`guid: ${escapeYaml(note.guid)}`,
|
|
1595
|
-
`title: ${escapeYaml(note.title)}`,
|
|
1596
|
-
`createdAt: ${note.createdAt}`,
|
|
1597
|
-
`updatedAt: ${note.updatedAt}`,
|
|
1598
|
-
`sourceType: ${note.sourceType}`,
|
|
1599
|
-
`recordingDurationSeconds: ${note.recordingDurationSeconds}`,
|
|
1600
|
-
`webUrl: ${note.webUrl}`
|
|
1601
|
-
];
|
|
1602
|
-
if (note.recordingStartAt) fm.push(`recordingStartAt: ${note.recordingStartAt}`);
|
|
1603
|
-
if (note.recordingEndAt) fm.push(`recordingEndAt: ${note.recordingEndAt}`);
|
|
1604
|
-
fm.push("---", "");
|
|
1605
|
-
const parts = [fm.join("\n"), `# ${note.title}`, ""];
|
|
1606
|
-
if (note.participants && note.participants.length > 0) {
|
|
1607
|
-
parts.push("## Participants", "");
|
|
1608
|
-
for (const p of note.participants) {
|
|
1609
|
-
const name = p.name ?? "(no name)";
|
|
1610
|
-
const email = p.email ? ` <${p.email}>` : "";
|
|
1611
|
-
parts.push(`- ${name}${email}`);
|
|
1612
|
-
}
|
|
1613
|
-
parts.push("");
|
|
1614
|
-
}
|
|
1615
|
-
if (opts.includeTranscript && opts.paragraphs && opts.paragraphs.length > 0) {
|
|
1616
|
-
const mcp = buildMcpTranscript(note, opts.paragraphs);
|
|
1617
|
-
const transcriptBody = renderTranscriptMarkdown(mcp);
|
|
1618
|
-
const startsWithHeader = transcriptBody.startsWith(`# ${note.title}`);
|
|
1619
|
-
const trimmed = startsWithHeader ? transcriptBody.slice(transcriptBody.indexOf("\n") + 1) : transcriptBody;
|
|
1620
|
-
parts.push(trimmed.replace(/^\s*\n+/, ""));
|
|
1621
|
-
}
|
|
1622
|
-
return `${parts.join("\n").trimEnd()}
|
|
1623
|
-
`;
|
|
1624
|
-
}
|
|
1625
|
-
function formatJson(note, opts) {
|
|
1626
|
-
const out = { ...note };
|
|
1627
|
-
if (opts.includeTranscript && opts.paragraphs) {
|
|
1628
|
-
out["transcript"] = { paragraphs: paragraphsToMcp(opts.paragraphs) };
|
|
1629
|
-
}
|
|
1630
|
-
return `${JSON.stringify(out, null, 2)}
|
|
1631
|
-
`;
|
|
1632
|
-
}
|
|
1633
|
-
function formatText(note, opts) {
|
|
1634
|
-
if (!opts.paragraphs || opts.paragraphs.length === 0) {
|
|
1635
|
-
return `${note.title}
|
|
1636
|
-
${note.webUrl}
|
|
1637
|
-
`;
|
|
1638
|
-
}
|
|
1639
|
-
const mcp = buildMcpTranscript(note, opts.paragraphs);
|
|
1640
|
-
return renderTranscriptText(mcp);
|
|
1641
|
-
}
|
|
1642
|
-
function escapeYaml(s) {
|
|
1643
|
-
if (/[:#\n"']/.test(s)) {
|
|
1644
|
-
return JSON.stringify(s);
|
|
1645
|
-
}
|
|
1646
|
-
return s;
|
|
1647
|
-
}
|
|
1648
|
-
|
|
1649
|
-
// src/commands/notes/get.ts
|
|
1650
|
-
var ALLOWED_INCLUDES = /* @__PURE__ */ new Set(["transcript"]);
|
|
1651
|
-
var ParagraphsListSchema = SimpleListResponseSchema(ParagraphSchema);
|
|
1652
|
-
var ParagraphsCursorSchema = PageCursorResponseSchema(ParagraphSchema);
|
|
1653
|
-
function registerNotesGet(parent) {
|
|
1654
|
-
parent.command("get <guid>").description("Get a single note. Outputs to stdout, or saves to a file with --output.").option("--output <path>", "Write to file (stdout becomes a single metadata line)").option(
|
|
1655
|
-
"--format <md|json|txt>",
|
|
1656
|
-
"Output format (default: md for TTY, json when piped)"
|
|
1657
|
-
).option(
|
|
1658
|
-
"--include <items>",
|
|
1659
|
-
"Comma-separated extras (v0.2 supports: transcript)",
|
|
1660
|
-
""
|
|
1661
|
-
).option("--force", "Overwrite existing file at --output path").addHelpText("after", `
|
|
156
|
+
`;function iA(e){e.command("search [keyword]").description("Deep keyword search \u2014 returns notes hydrated with their primary documents.").option("--keyword <text>",'Alternative to positional keyword (e.g. --keyword "Q3 Planning")').option("--folder <id>","Restrict hits to a folder and its descendants").option("--since <date>","Inclusive lower bound on createdAt (ISO-8601 or relative: 7d, 24h, 30m)").option("--until <date>","Exclusive upper bound on createdAt").option("--limit <n>",`Max results per page (default ${oA}, max ${nA})`).option("--cursor <token>","Continue a previous page (reserved \u2014 backend currently always null)").option("--workspace <guid>","Search within one workspace (see `tiro wiki workspaces`). Default: the workspace your API key is bound to, else across all your workspaces").option("--include-untitled","Include placeholder notes. Default: hidden",!1).addHelpText("after",Mt).action(async(A,t,r)=>{let n=r.optsWithGlobals(),i=(A??t.keyword??"").trim();if(!i)throw new f({code:"missing_keyword",message:"search requires a keyword (positional or --keyword).",errorType:"bad_request",suggestion:'tiro notes search "OKR"'},u.Usage);let s={};t.folder&&(s.folderId=t.folder),t.since&&(s.createdAtFrom=I(t.since)),t.until&&(s.createdAtTo=I(t.until));let c={},d=Nt(t.limit);d!==void 0&&(c.size=d),t.cursor&&(c.cursor=t.cursor);let p={keyword:i};Object.keys(s).length>0&&(p.filter=s),Object.keys(c).length>0&&(p.pagination=c);let l=k({...n.hostname!==void 0&&{hostnameOverride:n.hostname}}),w=await N(l,t.workspace);!w&&n.quiet!==!0&&process.stderr.write(`${a("\u26A0","yellow",n)} ${Z}
|
|
157
|
+
`);let b=w?`/v1/external/workspaces/${encodeURIComponent(w)}/notes/search`:"/v1/external/notes/search",h=await l.postJson(b,Wt,p),T=t.includeUntitled===!0?h.notes:h.notes.filter(J);if(y(n)==="json"){for(let Ae of T)C(Ae);h.nextCursor&&C({_cursor:h.nextCursor}),h.degraded===!0&&C({_degraded:!0,_degradedReason:h.degradedReason??null})}else Gt(T,h.nextCursor,n),h.degraded===!0&&n.quiet!==!0&&process.stderr.write(`${a("\u26A0","yellow",n)} search results are partial${h.degradedReason?` (${h.degradedReason})`:""}
|
|
158
|
+
`)})}function Nt(e){if(!e)return;let A=parseInt(e,10);return!Number.isFinite(A)||A<=0?oA:Math.min(A,nA)}function Gt(e,A,t){if(e.length===0){process.stdout.write(`${a("(no matches)","gray",t)}
|
|
159
|
+
`);return}for(let r of e){let n=r.createdAt.slice(0,10);process.stdout.write(`${a(n,"gray",t)} ${a(r.guid,"dim",t)} ${r.title}
|
|
160
|
+
`)}A&&process.stdout.write(`${a(`
|
|
161
|
+
next: --cursor ${A}`,"gray",t)}
|
|
162
|
+
`)}import"commander";import{mkdir as Vt,rename as Ut,stat as jt,writeFile as Qt,access as zt}from"fs/promises";import{dirname as Yt,resolve as Kt,sep as ki}from"path";function Zt(e){if(e.trim()==="")throw sA("output path is empty",e);let A=e.split(/[\\/]/);for(let t of A)if(t==="..")throw sA("path traversal segment '..' is not allowed",e)}function sA(e,A){return new f({code:"unsafe_output_path",message:`Refusing to write to ${JSON.stringify(A)}: ${e}.`,errorType:"bad_request",suggestion:"Use a path without '..' segments, or pass --force if you really mean it."},u.Usage)}async function $(e,A,t={}){t.force||Zt(e);let r=Kt(e);if(await Vt(Yt(r),{recursive:!0}),!t.force&&await Jt(r))throw new f({code:"file_exists",message:`File already exists: ${r}`,errorType:"conflict",suggestion:"Use --force to overwrite, or pick a different --output."},u.Generic);let n=`${r}.tmp.${process.pid}.${Date.now()}`;await Qt(n,A,"utf8"),await Ut(n,r);let i=await jt(r);return{path:r,size:i.size}}async function Jt(e){try{return await zt(e),!0}catch{return!1}}function U(e,A){return{noteGuid:e.guid,title:e.title,participants:e.participants?.map(t=>t.name||t.email||"").filter(t=>typeof t=="string"&&t.length>0)??[],createdAt:e.createdAt,recordingDurationSeconds:e.recordingDurationSeconds,paragraphs:j(A)}}function j(e){return e.map(A=>({timeFrom:A.timeFrom??null,timeTo:A.timeTo??null,segments:$t(A)})).filter(A=>A.segments.length>0)}function $t(e){let A=e.diarizedSegments;if(A&&A.length>0)return A.map(r=>({content:pe(r.content),speaker:{label:r.speaker.label,name:r.speaker.personName?pe(r.speaker.personName):null}})).filter(r=>r.content.length>0);let t=pe(e.transcript?.content??"");return t?[{content:t,speaker:null}]:[]}function de(e){return`${JSON.stringify(e,null,2)}
|
|
163
|
+
`}function _(e,A={}){let t=A.timestamps!==!1,r=t?_t(e):null,n=[];n.push(`# ${e.title}`,""),e.participants.length>0&&n.push(`**Participants**: ${e.participants.join(", ")}`,""),n.push("## Transcript","");for(let i of e.paragraphs){if(t){let s=er(i.timeFrom,r);s&&n.push(`### ${s}`,"")}for(let s of i.segments){let c=s.speaker?.name??s.speaker?.label??"Unknown";n.push(`**${c}**: ${s.content}`)}n.push("")}return`${n.join(`
|
|
164
|
+
`).trimEnd()}
|
|
165
|
+
`}function ee(e){let A=[];for(let t of e.paragraphs)for(let r of t.segments){let n=r.speaker?.name??r.speaker?.label??"Unknown";A.push(`[${n}] ${r.content}`)}return`${A.join(`
|
|
166
|
+
`)}
|
|
167
|
+
`}function _t(e){for(let A of e.paragraphs)if(A.timeFrom)return A.timeFrom;return null}function er(e,A){if(!e||!A)return"";let t=Date.parse(e),r=Date.parse(A);if(!Number.isFinite(t)||!Number.isFinite(r))return"";let n=Math.max(0,Math.floor((t-r)/1e3)),i=Math.floor(n/3600),s=Math.floor(n%3600/60),c=n%60;return i>0?`${V(i)}:${V(s)}:${V(c)}`:`${V(s)}:${V(c)}`}function V(e){return e.toString().padStart(2,"0")}function pe(e){return e.replace(/<[^>]*>/g,"").replace(/ /g," ").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").trim()}function cA(e,A,t={}){switch(A){case"md":return Ar(e,t);case"json":return tr(e,t);case"txt":return rr(e,t)}}function Ar(e,A){let t=["---",`guid: ${aA(e.guid)}`,`title: ${aA(e.title)}`,`createdAt: ${e.createdAt}`,`updatedAt: ${e.updatedAt}`,`sourceType: ${e.sourceType}`,`recordingDurationSeconds: ${e.recordingDurationSeconds}`,`webUrl: ${e.webUrl}`];e.recordingStartAt&&t.push(`recordingStartAt: ${e.recordingStartAt}`),e.recordingEndAt&&t.push(`recordingEndAt: ${e.recordingEndAt}`),t.push("---","");let r=[t.join(`
|
|
168
|
+
`),`# ${e.title}`,""];if(e.participants&&e.participants.length>0){r.push("## Participants","");for(let n of e.participants){let i=n.name??"(no name)",s=n.email?` <${n.email}>`:"";r.push(`- ${i}${s}`)}r.push("")}if(A.includeTranscript&&A.paragraphs&&A.paragraphs.length>0){let n=U(e,A.paragraphs),i=_(n),c=i.startsWith(`# ${e.title}`)?i.slice(i.indexOf(`
|
|
169
|
+
`)+1):i;r.push(c.replace(/^\s*\n+/,""))}return`${r.join(`
|
|
170
|
+
`).trimEnd()}
|
|
171
|
+
`}function tr(e,A){let t={...e};return A.includeTranscript&&A.paragraphs&&(t.transcript={paragraphs:j(A.paragraphs)}),`${JSON.stringify(t,null,2)}
|
|
172
|
+
`}function rr(e,A){if(!A.paragraphs||A.paragraphs.length===0)return`${e.title}
|
|
173
|
+
${e.webUrl}
|
|
174
|
+
`;let t=U(e,A.paragraphs);return ee(t)}function aA(e){return/[:#\n"']/.test(e)?JSON.stringify(e):e}var or=new Set(["transcript"]),nr=S(F),pA=R(F);function dA(e){e.command("get <guid>").description("Get a single note. Outputs to stdout, or saves to a file with --output.").option("--output <path>","Write to file (stdout becomes a single metadata line)").option("--format <md|json|txt>","Output format (default: md for TTY, json when piped)").option("--include <items>","Comma-separated extras (v0.2 supports: transcript)","").option("--force","Overwrite existing file at --output path").addHelpText("after",`
|
|
1662
175
|
Examples:
|
|
1663
176
|
tiro notes get <guid> # markdown to stdout
|
|
1664
177
|
tiro notes get <guid> --include transcript # add speaker-attributed paragraphs
|
|
@@ -1668,142 +181,10 @@ Examples:
|
|
|
1668
181
|
Tip for agents: prefer --output <path>. The actual content goes to disk
|
|
1669
182
|
and stdout collapses to a single metadata line, keeping your context
|
|
1670
183
|
window light.
|
|
1671
|
-
`).action(async (
|
|
1672
|
-
const globalOpts = cmd.optsWithGlobals();
|
|
1673
|
-
const includes = parseIncludes(opts.include);
|
|
1674
|
-
validateIncludes(includes);
|
|
1675
|
-
const format = pickFormat(opts.format, opts.output);
|
|
1676
|
-
const client = createApiClient({
|
|
1677
|
-
...globalOpts.hostname !== void 0 && { hostnameOverride: globalOpts.hostname }
|
|
1678
|
-
});
|
|
1679
|
-
const note = await client.getJson(`/v1/external/notes/${guid}`, NoteSchema);
|
|
1680
|
-
let paragraphs;
|
|
1681
|
-
if (includes.has("transcript") || format === "txt") {
|
|
1682
|
-
paragraphs = await fetchAllParagraphs(client, guid);
|
|
1683
|
-
}
|
|
1684
|
-
const content = formatNote(note, format, {
|
|
1685
|
-
includeTranscript: includes.has("transcript"),
|
|
1686
|
-
...paragraphs !== void 0 && { paragraphs }
|
|
1687
|
-
});
|
|
1688
|
-
if (opts.output) {
|
|
1689
|
-
const result = await writeFileAtomic(opts.output, content, {
|
|
1690
|
-
...opts.force === true && { force: true }
|
|
1691
|
-
});
|
|
1692
|
-
printOutput(
|
|
1693
|
-
{
|
|
1694
|
-
ok: true,
|
|
1695
|
-
data: {
|
|
1696
|
-
saved: result.path,
|
|
1697
|
-
size: result.size,
|
|
1698
|
-
format,
|
|
1699
|
-
guid: note.guid,
|
|
1700
|
-
title: note.title
|
|
1701
|
-
}
|
|
1702
|
-
},
|
|
1703
|
-
globalOpts
|
|
1704
|
-
);
|
|
1705
|
-
return;
|
|
1706
|
-
}
|
|
1707
|
-
const mode = resolveOutputMode(globalOpts);
|
|
1708
|
-
if (mode === "json" && format !== "json") {
|
|
1709
|
-
printOutput(
|
|
1710
|
-
{
|
|
1711
|
-
ok: true,
|
|
1712
|
-
data: {
|
|
1713
|
-
...note,
|
|
1714
|
-
...paragraphs && { transcript: { paragraphs: paragraphsToMcp(paragraphs) } }
|
|
1715
|
-
}
|
|
1716
|
-
},
|
|
1717
|
-
globalOpts
|
|
1718
|
-
);
|
|
1719
|
-
} else if (format === "json") {
|
|
1720
|
-
process.stdout.write(content);
|
|
1721
|
-
} else {
|
|
1722
|
-
if (process.stdout.isTTY && format === "txt") {
|
|
1723
|
-
process.stdout.write(`${color(`# ${note.title}`, "bold", globalOpts)}
|
|
1724
|
-
|
|
1725
|
-
`);
|
|
1726
|
-
}
|
|
1727
|
-
process.stdout.write(content);
|
|
1728
|
-
}
|
|
1729
|
-
});
|
|
1730
|
-
}
|
|
1731
|
-
async function fetchAllParagraphs(client, guid) {
|
|
1732
|
-
const first = await client.getJson(
|
|
1733
|
-
`/v1/external/notes/${guid}/paragraphs`,
|
|
1734
|
-
ParagraphsCursorSchema.or(ParagraphsListSchema)
|
|
1735
|
-
);
|
|
1736
|
-
const all = [...first.content];
|
|
1737
|
-
if ("nextCursor" in first) {
|
|
1738
|
-
let cursor = first.nextCursor;
|
|
1739
|
-
while (cursor) {
|
|
1740
|
-
const next = await client.getJson(
|
|
1741
|
-
`/v1/external/notes/${guid}/paragraphs`,
|
|
1742
|
-
ParagraphsCursorSchema,
|
|
1743
|
-
{ cursor }
|
|
1744
|
-
);
|
|
1745
|
-
all.push(...next.content);
|
|
1746
|
-
cursor = next.nextCursor;
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
1749
|
-
return all;
|
|
1750
|
-
}
|
|
1751
|
-
function parseIncludes(raw) {
|
|
1752
|
-
if (!raw) return /* @__PURE__ */ new Set();
|
|
1753
|
-
return new Set(
|
|
1754
|
-
raw.split(",").map((s) => s.trim().toLowerCase()).filter((s) => s.length > 0)
|
|
1755
|
-
);
|
|
1756
|
-
}
|
|
1757
|
-
function validateIncludes(includes) {
|
|
1758
|
-
for (const inc of includes) {
|
|
1759
|
-
if (!ALLOWED_INCLUDES.has(inc)) {
|
|
1760
|
-
throw new TiroError(
|
|
1761
|
-
{
|
|
1762
|
-
code: "invalid_include",
|
|
1763
|
-
message: `Invalid --include "${inc}". v0.2.0 supports: transcript.`,
|
|
1764
|
-
errorType: "bad_request",
|
|
1765
|
-
suggestion: "Use --include transcript"
|
|
1766
|
-
},
|
|
1767
|
-
ExitCode.Usage
|
|
1768
|
-
);
|
|
1769
|
-
}
|
|
1770
|
-
}
|
|
1771
|
-
}
|
|
1772
|
-
function pickFormat(format, output) {
|
|
1773
|
-
const allowed = ["md", "json", "txt"];
|
|
1774
|
-
if (format) {
|
|
1775
|
-
const f = format.toLowerCase();
|
|
1776
|
-
if (!allowed.includes(f)) {
|
|
1777
|
-
throw new TiroError(
|
|
1778
|
-
{
|
|
1779
|
-
code: "invalid_format",
|
|
1780
|
-
message: `Invalid --format "${format}". Allowed: md, json, txt.`,
|
|
1781
|
-
errorType: "bad_request"
|
|
1782
|
-
},
|
|
1783
|
-
ExitCode.Usage
|
|
1784
|
-
);
|
|
1785
|
-
}
|
|
1786
|
-
return f;
|
|
1787
|
-
}
|
|
1788
|
-
if (output) {
|
|
1789
|
-
if (output.endsWith(".json")) return "json";
|
|
1790
|
-
if (output.endsWith(".txt")) return "txt";
|
|
1791
|
-
return "md";
|
|
1792
|
-
}
|
|
1793
|
-
return process.stdout.isTTY ? "md" : "json";
|
|
1794
|
-
}
|
|
184
|
+
`).action(async(A,t,r)=>{let n=r.optsWithGlobals(),i=sr(t.include);ar(i);let s=cr(t.format,t.output),c=k({...n.hostname!==void 0&&{hostnameOverride:n.hostname}}),d=await c.getJson(`/v1/external/notes/${A}`,L),p;(i.has("transcript")||s==="txt")&&(p=await ir(c,A));let l=cA(d,s,{includeTranscript:i.has("transcript"),...p!==void 0&&{paragraphs:p}});if(t.output){let b=await $(t.output,l,{...t.force===!0&&{force:!0}});v({ok:!0,data:{saved:b.path,size:b.size,format:s,guid:d.guid,title:d.title}},n);return}y(n)==="json"&&s!=="json"?v({ok:!0,data:{...d,...p&&{transcript:{paragraphs:j(p)}}}},n):(s==="json"||process.stdout.isTTY&&s==="txt"&&process.stdout.write(`${a(`# ${d.title}`,"bold",n)}
|
|
1795
185
|
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
var ParagraphsListSchema2 = SimpleListResponseSchema(ParagraphSchema);
|
|
1799
|
-
var ParagraphsCursorSchema2 = PageCursorResponseSchema(ParagraphSchema);
|
|
1800
|
-
function registerNotesTranscript(parent) {
|
|
1801
|
-
parent.command("transcript <guid>").description(
|
|
1802
|
-
"Get the full transcript of a note as speaker-attributed paragraphs.\nJSON output matches MCP get_note_transcript shape exactly."
|
|
1803
|
-
).option("--output <path>", "Write to file (stdout = single metadata line)").option(
|
|
1804
|
-
"--format <md|json|txt>",
|
|
1805
|
-
"Output format (default: md if TTY, txt when piped; json mirrors MCP)"
|
|
1806
|
-
).option("--force", "Overwrite existing file at --output path").option("--no-timestamps", "Omit paragraph timestamp headers (md format only)").addHelpText("after", `
|
|
186
|
+
`),process.stdout.write(l))})}async function ir(e,A){let t=await e.getJson(`/v1/external/notes/${A}/paragraphs`,pA.or(nr)),r=[...t.content];if("nextCursor"in t){let n=t.nextCursor;for(;n;){let i=await e.getJson(`/v1/external/notes/${A}/paragraphs`,pA,{cursor:n});r.push(...i.content),n=i.nextCursor}}return r}function sr(e){return e?new Set(e.split(",").map(A=>A.trim().toLowerCase()).filter(A=>A.length>0)):new Set}function ar(e){for(let A of e)if(!or.has(A))throw new f({code:"invalid_include",message:`Invalid --include "${A}". v0.2.0 supports: transcript.`,errorType:"bad_request",suggestion:"Use --include transcript"},u.Usage)}function cr(e,A){let t=["md","json","txt"];if(e){let r=e.toLowerCase();if(!t.includes(r))throw new f({code:"invalid_format",message:`Invalid --format "${e}". Allowed: md, json, txt.`,errorType:"bad_request"},u.Usage);return r}return A?A.endsWith(".json")?"json":A.endsWith(".txt")?"txt":"md":process.stdout.isTTY?"md":"json"}import"commander";var pr=S(F),lA=R(F);function uA(e){e.command("transcript <guid>").description(`Get the full transcript of a note as speaker-attributed paragraphs.
|
|
187
|
+
JSON output matches MCP get_note_transcript shape exactly.`).option("--output <path>","Write to file (stdout = single metadata line)").option("--format <md|json|txt>","Output format (default: md if TTY, txt when piped; json mirrors MCP)").option("--force","Overwrite existing file at --output path").option("--no-timestamps","Omit paragraph timestamp headers (md format only)").addHelpText("after",`
|
|
1807
188
|
Examples:
|
|
1808
189
|
tiro notes transcript <guid> # md in TTY, txt in pipe
|
|
1809
190
|
tiro notes transcript <guid> --format md --output ./t.md
|
|
@@ -1813,215 +194,119 @@ Examples:
|
|
|
1813
194
|
|
|
1814
195
|
The --format json output is byte-for-byte identical to MCP's
|
|
1815
196
|
get_note_transcript so agents can swap surfaces without changing parsers.
|
|
1816
|
-
`).action(async (
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
ParagraphsCursorSchema2,
|
|
1866
|
-
{ cursor }
|
|
1867
|
-
);
|
|
1868
|
-
all.push(...next.content);
|
|
1869
|
-
cursor = next.nextCursor;
|
|
1870
|
-
}
|
|
1871
|
-
}
|
|
1872
|
-
return all;
|
|
1873
|
-
}
|
|
1874
|
-
function pickFormat2(format, output, globalOpts) {
|
|
1875
|
-
const allowed = ["md", "json", "txt"];
|
|
1876
|
-
if (format) {
|
|
1877
|
-
const f = format.toLowerCase();
|
|
1878
|
-
if (!allowed.includes(f)) {
|
|
1879
|
-
throw new TiroError(
|
|
1880
|
-
{
|
|
1881
|
-
code: "invalid_format",
|
|
1882
|
-
message: `Invalid --format "${format}". Allowed: md, json, txt.`,
|
|
1883
|
-
errorType: "bad_request"
|
|
1884
|
-
},
|
|
1885
|
-
ExitCode.Usage
|
|
1886
|
-
);
|
|
1887
|
-
}
|
|
1888
|
-
return f;
|
|
1889
|
-
}
|
|
1890
|
-
if (output) {
|
|
1891
|
-
if (output.endsWith(".json")) return "json";
|
|
1892
|
-
if (output.endsWith(".md")) return "md";
|
|
1893
|
-
if (output.endsWith(".txt")) return "txt";
|
|
1894
|
-
return "md";
|
|
1895
|
-
}
|
|
1896
|
-
if (globalOpts.json) return "json";
|
|
1897
|
-
if (globalOpts.pretty) return "md";
|
|
1898
|
-
return process.stdout.isTTY ? "md" : "txt";
|
|
1899
|
-
}
|
|
197
|
+
`).action(async(A,t,r)=>{let n=r.optsWithGlobals(),i=lr(t.format,t.output,n),s=k({...n.hostname!==void 0&&{hostnameOverride:n.hostname}}),c=await s.getJson(`/v1/external/notes/${A}`,L),d=await dr(s,A),p=U(c,d),l=i==="json"?de(p):i==="md"?_(p,{timestamps:t.timestamps!==!1}):ee(p);if(t.output){let b=await $(t.output,l,{...t.force===!0&&{force:!0}});v({ok:!0,data:{saved:b.path,size:b.size,format:i,guid:c.guid,paragraphCount:p.paragraphs.length,segmentCount:p.paragraphs.reduce((h,T)=>h+T.segments.length,0)}},n);return}y(n)==="json"&&i!=="json"?v({ok:!0,data:p},n):process.stdout.write(l)})}async function dr(e,A){let t=await e.getJson(`/v1/external/notes/${A}/paragraphs`,lA.or(pr)),r=[...t.content];if("nextCursor"in t){let n=t.nextCursor;for(;n;){let i=await e.getJson(`/v1/external/notes/${A}/paragraphs`,lA,{cursor:n});r.push(...i.content),n=i.nextCursor}}return r}function lr(e,A,t){let r=["md","json","txt"];if(e){let n=e.toLowerCase();if(!r.includes(n))throw new f({code:"invalid_format",message:`Invalid --format "${e}". Allowed: md, json, txt.`,errorType:"bad_request"},u.Usage);return n}return A?A.endsWith(".json")?"json":A.endsWith(".md")?"md":A.endsWith(".txt")?"txt":"md":t.json?"json":t.pretty||process.stdout.isTTY?"md":"txt"}function mA(e){let A=e.command("notes").description("List, search, and download notes");rA(A),iA(A),dA(A),uA(A)}import"commander";import"commander";import{z as ur}from"zod";var mr=ur.array(Qe),gr=`
|
|
198
|
+
Examples:
|
|
199
|
+
tiro folders list
|
|
200
|
+
tiro folders list --workspace <guid> --json
|
|
201
|
+
|
|
202
|
+
Lists the folders in a workspace (flat). sharingType is PRIVATE / ALL_MEMBER_VIEWER
|
|
203
|
+
/ ALL_MEMBER_EDITOR / LIMITED. Pass a folder id to 'tiro notes list --folder <id>'.
|
|
204
|
+
The workspace resolves from --workspace, else your API key's bound workspace; an
|
|
205
|
+
unbound credential must pass --workspace (run 'tiro wiki workspaces' for guids).
|
|
206
|
+
`;function gA(e){e.command("list").description("List folders in a workspace.").option("--workspace <guid>","Workspace to list folders from (see `tiro wiki workspaces`). Default: your API key's bound workspace").addHelpText("after",gr).action(async(A,t)=>{let r=t.optsWithGlobals(),n=k({...r.hostname!==void 0&&{hostnameOverride:r.hostname}}),i=await D(n,A.workspace),s=await n.getJson(`/v1/external/workspaces/${encodeURIComponent(i)}/folders`,mr);if(y(r)==="json")for(let d of s)C(d);else fr(s,r)})}function fr(e,A){if(e.length===0){process.stdout.write(`${a("(no folders)","gray",A)}
|
|
207
|
+
`);return}for(let t of e)process.stdout.write(`${a(t.id,"dim",A)} ${a(t.sharingType,"gray",A)} ${t.title}
|
|
208
|
+
`)}import"commander";var hr=S(ae),wr=`
|
|
209
|
+
Examples:
|
|
210
|
+
tiro folders tree
|
|
211
|
+
tiro folders tree --workspace <guid> --json
|
|
212
|
+
|
|
213
|
+
Returns the folder hierarchy for a workspace. In JSON mode each root folder is one
|
|
214
|
+
NDJSON line with nested children; in pretty mode the tree is indented by depth.
|
|
215
|
+
`;function fA(e){e.command("tree").description("Show the folder hierarchy of a workspace.").option("--workspace <guid>","Workspace to read the tree from (see `tiro wiki workspaces`). Default: your API key's bound workspace").addHelpText("after",wr).action(async(A,t)=>{let r=t.optsWithGlobals(),n=k({...r.hostname!==void 0&&{hostnameOverride:r.hostname}}),i=await D(n,A.workspace),s=await n.getJson(`/v1/external/workspaces/${encodeURIComponent(i)}/folders/tree`,hr);if(y(r)==="json")for(let d of s.content)C(d);else br(s.content,r)})}function br(e,A){if(e.length===0){process.stdout.write(`${a("(no folders)","gray",A)}
|
|
216
|
+
`);return}for(let t of e)hA(t,A)}function hA(e,A){let t=" ".repeat(e.depth),r=e.isAccessible?e.title:a(`${e.title} (no access)`,"gray",A);process.stdout.write(`${t}${a(e.id,"dim",A)} ${r}
|
|
217
|
+
`);for(let n of e.children)hA(n,A)}function wA(e){let A=e.command("folders").description("List workspace folders (flat or as a tree) to scope note queries");gA(A),fA(A)}import"commander";import"commander";var yr=R(ze),bA=1e3,kr=`
|
|
218
|
+
Examples:
|
|
219
|
+
tiro word-memories list
|
|
220
|
+
tiro word-memories list --workspace <guid> --json
|
|
221
|
+
|
|
222
|
+
Lists a workspace's custom word memories (vocabulary that biases transcription).
|
|
223
|
+
The workspace resolves from --workspace, else your API key's bound workspace; an
|
|
224
|
+
unbound credential must pass --workspace (run 'tiro wiki workspaces' for guids).
|
|
225
|
+
`;function yA(e){e.command("list").description("List a workspace's custom word memories.").option("--workspace <guid>","Workspace to list from (see `tiro wiki workspaces`). Default: your API key's bound workspace").option("--limit <n>",`Max results per page (max ${bA})`).option("--cursor <token>","Continue a previous page").addHelpText("after",kr).action(async(A,t)=>{let r=t.optsWithGlobals(),n=k({...r.hostname!==void 0&&{hostnameOverride:r.hostname}}),i=await D(n,A.workspace),s={},c=Cr(A.limit);c!==void 0&&(s.size=c),A.cursor&&(s.cursor=A.cursor);let d=await n.getJson(`/v1/external/workspaces/${encodeURIComponent(i)}/word-memories`,yr,s);if(y(r)==="json"){for(let l of d.content)C(l);d.nextCursor&&C({_cursor:d.nextCursor})}else vr(d.content,d.nextCursor,r)})}function Cr(e){if(!e)return;let A=parseInt(e,10);if(!(!Number.isFinite(A)||A<=0))return Math.min(A,bA)}function vr(e,A,t){if(e.length===0){process.stdout.write(`${a("(no word memories)","gray",t)}
|
|
226
|
+
`);return}for(let r of e)process.stdout.write(`${a(String(r.id),"dim",t)} ${r.entry}
|
|
227
|
+
`);A&&process.stdout.write(`${a(`
|
|
228
|
+
next: --cursor ${A}`,"gray",t)}
|
|
229
|
+
`)}function kA(e){let A=e.command("word-memories").description("List a workspace's custom word memories (transcription vocabulary)");yA(A)}import"commander";import"commander";var vA=100,CA=500,Tr=`
|
|
230
|
+
Examples:
|
|
231
|
+
tiro wiki search "onboarding"
|
|
232
|
+
tiro wiki search "payment gateway" --size 50 --json
|
|
233
|
+
tiro wiki search "billing" --workspace <guid>
|
|
234
|
+
|
|
235
|
+
The workspace defaults to the one implicit in your API key. Pass --workspace
|
|
236
|
+
to target a specific workspace (get guids from 'tiro wiki workspaces').
|
|
237
|
+
Results are ranked wiki pages (pageGuid, name, type, relevance). Pass a
|
|
238
|
+
pageGuid to 'tiro wiki page', 'tiro wiki mentions', or 'tiro wiki graph'.
|
|
239
|
+
`;function TA(e){e.command("search <query>").description("Search the workspace wiki for pages matching a keyword.").option("--size <n>",`Max results (default: backend default, max ${vA})`).option("--workspace <guid>","Target a specific workspace (from `tiro wiki workspaces`); defaults to your default workspace").addHelpText("after",Tr).action(async(A,t,r)=>{let n=r.optsWithGlobals(),i=A.trim();if(!i)throw new f({code:"missing_query",message:"wiki search requires a query.",errorType:"bad_request",suggestion:'tiro wiki search "onboarding"'},u.Usage);let s=k({...n.hostname!==void 0&&{hostnameOverride:n.hostname}}),c=t.workspace??await E(s),p={q:i.length>CA?i.slice(0,CA):i},l=xr(t.size);l!==void 0&&(p.size=l);let w=await s.getJson(`/v1/external/workspaces/${encodeURIComponent(c)}/wiki/search/pages`,Ne,p);if(y(n)==="json")for(let h of w.items)C(h);else Br(w.items,n)})}function xr(e){if(!e)return;let A=parseInt(e,10);if(!(!Number.isFinite(A)||A<=0))return Math.min(A,vA)}function Br(e,A){if(e.length===0){process.stdout.write(`${a("(no matches)","gray",A)}
|
|
240
|
+
`);return}for(let t of e){let r=t.score.toFixed(2);process.stdout.write(`${a(r,"cyan",A)} ${a(t.guid,"dim",A)} ${a(t.pageType,"gray",A)} ${t.canonicalName}
|
|
241
|
+
`)}}import"commander";var Lr=`
|
|
242
|
+
Examples:
|
|
243
|
+
tiro wiki page <pageGuid>
|
|
244
|
+
tiro wiki page <pageGuid> --json
|
|
245
|
+
tiro wiki page <pageGuid> --workspace <guid>
|
|
1900
246
|
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
registerNotesGet(notes);
|
|
1907
|
-
registerNotesTranscript(notes);
|
|
1908
|
-
}
|
|
247
|
+
Returns the page body (per-user description) plus metadata, mentions,
|
|
248
|
+
aliases, and links. The workspace defaults to the one implicit in your API
|
|
249
|
+
key; pass --workspace to target a specific workspace (from 'tiro wiki workspaces').
|
|
250
|
+
`;function xA(e){e.command("page <pageGuid>").description("Get a single wiki page: body, metadata, mentions, aliases, and links.").option("--workspace <guid>","Target a specific workspace (from `tiro wiki workspaces`); defaults to your default workspace").addHelpText("after",Lr).action(async(A,t,r)=>{let n=r.optsWithGlobals(),i=k({...n.hostname!==void 0&&{hostnameOverride:n.hostname}}),s=t.workspace??await E(i),c=await i.getJson(`/v1/external/workspaces/${encodeURIComponent(s)}/wiki/pages/${encodeURIComponent(A)}`,Ve);y(n)==="json"?v({ok:!0,data:c},n):Rr(c,n)})}function Rr(e,A){let t=process.stdout.write.bind(process.stdout);if(t(`${a(e.canonicalName,"bold",A)} ${a(`(${e.pageType})`,"gray",A)}
|
|
251
|
+
`),t(`${a(e.guid,"dim",A)}
|
|
1909
252
|
|
|
1910
|
-
|
|
1911
|
-
import "commander";
|
|
253
|
+
`),e.description)t(`${e.description}
|
|
1912
254
|
|
|
1913
|
-
|
|
1914
|
-
import "commander";
|
|
1915
|
-
function registerMcpInfo(parent) {
|
|
1916
|
-
parent.command("info").description("Show Tiro MCP endpoint and connection instructions").action((_opts, cmd) => {
|
|
1917
|
-
const globalOpts = cmd.optsWithGlobals();
|
|
1918
|
-
printOutput(
|
|
1919
|
-
{
|
|
1920
|
-
ok: true,
|
|
1921
|
-
data: {
|
|
1922
|
-
name: HOSTED_MCP_NAME,
|
|
1923
|
-
transport: "http",
|
|
1924
|
-
url: HOSTED_MCP_URL,
|
|
1925
|
-
docs: HOSTED_MCP_DOCS,
|
|
1926
|
-
install: {
|
|
1927
|
-
claudeCode: `claude mcp add --transport http ${HOSTED_MCP_NAME} ${HOSTED_MCP_URL}`
|
|
1928
|
-
}
|
|
1929
|
-
}
|
|
1930
|
-
},
|
|
1931
|
-
globalOpts
|
|
1932
|
-
);
|
|
1933
|
-
});
|
|
1934
|
-
}
|
|
255
|
+
`);else{let r=e.descriptionStatus??"none";t(`${a(`(no description \u2014 status: ${r})`,"gray",A)}
|
|
1935
256
|
|
|
1936
|
-
|
|
1937
|
-
import
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
}
|
|
1957
|
-
process.stdout.write(`${command}
|
|
1958
|
-
`);
|
|
1959
|
-
});
|
|
1960
|
-
}
|
|
257
|
+
`)}t(`${a("mentions","gray",A)} ${e.mentionCountVisible} ${a("aliases","gray",A)} ${e.aliases.length} ${a("links","gray",A)} ${e.links.length}
|
|
258
|
+
`)}import"commander";var BA=200,Er=`
|
|
259
|
+
Examples:
|
|
260
|
+
tiro wiki mentions <pageGuid>
|
|
261
|
+
tiro wiki mentions <pageGuid> --limit 100 --json
|
|
262
|
+
tiro wiki mentions <pageGuid> --workspace <guid>
|
|
263
|
+
|
|
264
|
+
Lists the note paragraphs that reference a wiki page. In JSON mode a trailing
|
|
265
|
+
{_cursor: ...} object is emitted when more pages are available; pass the
|
|
266
|
+
cursor token back via --cursor to fetch the next page. The workspace defaults
|
|
267
|
+
to the one implicit in your API key; pass --workspace to target a specific
|
|
268
|
+
workspace (from 'tiro wiki workspaces').
|
|
269
|
+
`;function LA(e){e.command("mentions <pageGuid>").description("List the note mentions that reference a single wiki page.").option("--limit <n>",`Max mentions per page (max ${BA})`).option("--cursor <token>","Continue from a previous page's cursor token.").option("--workspace <guid>","Target a specific workspace (from `tiro wiki workspaces`); defaults to your default workspace").addHelpText("after",Er).action(async(A,t,r)=>{let n=r.optsWithGlobals(),i=k({...n.hostname!==void 0&&{hostnameOverride:n.hostname}}),s=t.workspace??await E(i),c={},d=Ir(t.limit);d!==void 0&&(c.limit=d),t.cursor&&(c.cursor=t.cursor);let p=await i.getJson(`/v1/external/workspaces/${encodeURIComponent(s)}/wiki/pages/${encodeURIComponent(A)}/mentions`,Ue,c);if(y(n)==="json"){for(let b of p.items)C(b);let w=Or(p.nextCursorCreatedAt,p.nextCursorId);w&&C({_cursor:w})}else Xr(p.items,n)})}function Ir(e){if(!e)return;let A=parseInt(e,10);if(!(!Number.isFinite(A)||A<=0))return Math.min(A,BA)}function Or(e,A){if(e===null||A===null)return null;let t=Date.parse(e);return Number.isNaN(t)?null:`${t}_${A}`}function Xr(e,A){if(e.length===0){process.stdout.write(`${a("(no mentions)","gray",A)}
|
|
270
|
+
`);return}for(let t of e){let r=t.createdAt.slice(0,10);process.stdout.write(`${a(r,"gray",A)} ${a(t.kind,"cyan",A)} ${t.extractedText}
|
|
271
|
+
`)}}import"commander";var le=["seed","expand","around","links"],RA=500,Fr=50,Sr=`
|
|
272
|
+
Modes (--mode, default: around):
|
|
273
|
+
around Neighborhood around <pageGuid> (--radius hops, default 2)
|
|
274
|
+
expand Outward expansion from <pageGuid> (--depth hops, default 1)
|
|
275
|
+
links Links among <pageGuid> + each --page <guid> (repeatable)
|
|
276
|
+
seed Overview seed graph (no <pageGuid> required; --query, --type, --since)
|
|
1961
277
|
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
278
|
+
Examples:
|
|
279
|
+
tiro wiki graph <pageGuid> # around, radius 2
|
|
280
|
+
tiro wiki graph <pageGuid> --mode expand --depth 2
|
|
281
|
+
tiro wiki graph <pageGuid> --mode links --page <g2> --page <g3>
|
|
282
|
+
tiro wiki graph --mode seed --query "billing"
|
|
283
|
+
tiro wiki graph <pageGuid> --workspace <guid>
|
|
284
|
+
|
|
285
|
+
Returns a node+edge slice of the workspace wiki graph. The workspace defaults
|
|
286
|
+
to the one implicit in your API key; pass --workspace to target a specific
|
|
287
|
+
workspace (from 'tiro wiki workspaces').
|
|
288
|
+
`;function EA(e){e.command("graph [pageGuid]").description("Get a node+edge slice of the wiki link graph around a page.").option("--mode <mode>",`Graph mode: ${le.join(" | ")} (default: around)`).option("--depth <n>","expand mode: link-hops to traverse outward (default 1)").option("--radius <n>","around mode: neighborhood radius in hops (default 2)").option("--limit <n>","Max nodes to return (max 200)").option("--page <guid>","links mode: additional page guid (repeatable)",qr,[]).option("--type <type>","seed mode: restrict to a page type (CONCEPT|DECISION|ENTITY)").option("--since <date>","seed mode: only pages updated at/after this date").option("--query <keyword>","seed mode: keyword to seed the overview graph").option("--workspace <guid>","Target a specific workspace (from `tiro wiki workspaces`); defaults to your default workspace").addHelpText("after",Sr).action(async(A,t,r)=>{let n=r.optsWithGlobals(),i=Pr(t.mode);if((i==="expand"||i==="around")&&!A)throw new f({code:"missing_page_guid",message:`--mode ${i} requires a <pageGuid> positional argument.`,errorType:"bad_request",suggestion:`tiro wiki graph <pageGuid> --mode ${i}`},u.Usage);let s=k({...n.hostname!==void 0&&{hostnameOverride:n.hostname}}),c=t.workspace??await E(s),d=i==="links"?void 0:ue(t.limit,200)??Fr,p=await Dr(s,c,i,A??"",t,d),l=Hr(p,d??p.nodes.length);y(n)==="json"?v({ok:!0,data:l},n):Wr(l,n)})}async function Dr(e,A,t,r,n,i){let s=`/v1/external/workspaces/${encodeURIComponent(A)}/wiki/graph`;if(t==="expand")return e.getJson(`${s}/expand`,M,{pageGuid:r,depth:ue(n.depth,200),limit:i});if(t==="around")return e.getJson(`${s}/around`,M,{pageGuid:r,radius:ue(n.radius,200),limit:i});if(t==="links"){let p=[r,...n.page??[]].filter(l=>l&&l.length>0);return e.getJson(`${s}/links`,M,{pageGuids:p.join(",")})}let c=n.query,d=c&&c.length>RA?c.slice(0,RA):c;return e.getJson(`${s}/seed`,M,{type:n.type,since:n.since?I(n.since):void 0,q:d,limit:i})}function Pr(e){if(!e)return"around";let A=e.toLowerCase();if(le.includes(A))return A;throw new f({code:"invalid_mode",message:`Invalid --mode "${e}". Allowed: ${le.join(", ")}.`,errorType:"bad_request",suggestion:"tiro wiki graph <pageGuid> --mode around"},u.Usage)}function ue(e,A){if(!e)return;let t=parseInt(e,10);if(!(!Number.isFinite(t)||t<=0))return Math.min(t,A)}function qr(e,A){return[...A,e]}function Hr(e,A){let t=e.nodes.slice(0,A),r=new Set(t.map(s=>s.guid)),n=e.edges.filter(s=>r.has(s.sourcePageGuid)&&r.has(s.targetPageGuid)),i=e.nodes.length>A||n.length<e.edges.length;return{...e,nodes:t,edges:n,truncated:i}}function Wr(e,A){let t=process.stdout.write.bind(process.stdout);t(`${a("nodes","gray",A)} ${e.nodes.length} ${a("edges","gray",A)} ${e.edges.length}
|
|
289
|
+
`),e.truncated&&t(`${a(`(truncated \u2014 showing first ${e.nodes.length} nodes; narrow with --query / --type / smaller --radius)`,"gray",A)}
|
|
290
|
+
`),t(`
|
|
291
|
+
`);for(let r of e.nodes)t(`${a("\u25CF","cyan",A)} ${a(r.guid,"dim",A)} ${r.canonicalName}
|
|
292
|
+
`);e.edges.length>0&&t(`
|
|
293
|
+
`);for(let r of e.edges){let n=r.isDirectional?"\u2192":"\u2014";t(`${a(r.sourcePageGuid,"dim",A)} ${n} ${a(r.targetPageGuid,"dim",A)} ${a(r.linkType,"gray",A)}
|
|
294
|
+
`)}}import"commander";var Mr=`
|
|
295
|
+
Examples:
|
|
296
|
+
tiro wiki workspaces
|
|
297
|
+
tiro wiki workspaces --json
|
|
1971
298
|
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
import {
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
];
|
|
1982
|
-
var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
|
|
1983
|
-
async function startUpdateCheck() {
|
|
1984
|
-
if (process.env["NO_UPDATE_NOTIFIER"] === "1") return null;
|
|
1985
|
-
if (process.env["CI"]) return null;
|
|
1986
|
-
if (process.stdout.isTTY !== true) return null;
|
|
1987
|
-
const pkg = await loadPkg();
|
|
1988
|
-
if (!pkg) return null;
|
|
1989
|
-
try {
|
|
1990
|
-
const factory = updateNotifier;
|
|
1991
|
-
return factory({
|
|
1992
|
-
pkg,
|
|
1993
|
-
updateCheckInterval: ONE_DAY_MS,
|
|
1994
|
-
shouldNotifyInNpmScript: false
|
|
1995
|
-
});
|
|
1996
|
-
} catch {
|
|
1997
|
-
return null;
|
|
1998
|
-
}
|
|
1999
|
-
}
|
|
2000
|
-
function emitUpdateBanner(notifier) {
|
|
2001
|
-
if (!notifier) return;
|
|
2002
|
-
if (!notifier.update) return;
|
|
2003
|
-
notifier.notify({
|
|
2004
|
-
isGlobal: true,
|
|
2005
|
-
defer: false,
|
|
2006
|
-
message: "Update available {currentVersion} \u2192 {latestVersion}\nRun npm install -g {packageName} to update\n\nChangelog: https://www.npmjs.com/package/{packageName}?activeTab=versions"
|
|
2007
|
-
});
|
|
2008
|
-
}
|
|
2009
|
-
async function loadPkg() {
|
|
2010
|
-
for (const path of CANDIDATE_PATHS2) {
|
|
2011
|
-
try {
|
|
2012
|
-
const raw = await readFile(path, "utf8");
|
|
2013
|
-
const parsed = JSON.parse(raw);
|
|
2014
|
-
if (typeof parsed.name === "string" && typeof parsed.version === "string" && parsed.name.length > 0 && parsed.version.length > 0) {
|
|
2015
|
-
return { name: parsed.name, version: parsed.version };
|
|
2016
|
-
}
|
|
2017
|
-
} catch {
|
|
2018
|
-
}
|
|
2019
|
-
}
|
|
2020
|
-
return null;
|
|
2021
|
-
}
|
|
299
|
+
Lists all workspaces you are a member of. Use the guid with --workspace on
|
|
300
|
+
any other wiki command to target a non-default workspace.
|
|
301
|
+
`;function IA(e){e.command("workspaces").description("List all workspaces you are a member of (use guid with --workspace).").addHelpText("after",Mr).action(async(A,t)=>{let r=t.optsWithGlobals(),n=k({...r.hostname!==void 0&&{hostnameOverride:r.hostname}}),i=await Ze(n);if(y(r)==="json")for(let c of i)C(c);else Nr(i,r)})}function Nr(e,A){if(e.length===0){process.stdout.write(`${a("(no workspaces)","gray",A)}
|
|
302
|
+
`);return}for(let t of e){let r=t.isWikiEnabled?a("wiki:on","green",A):a("wiki:off","gray",A);process.stdout.write(`${a(t.guid,"dim",A)} ${r} ${t.name}
|
|
303
|
+
`)}}function OA(e){let A=e.command("wiki").description("Search and explore the workspace wiki (pages, mentions, link graph)");TA(A),xA(A),LA(A),EA(A),IA(A)}import"commander";import"commander";function XA(e){e.command("info").description("Show Tiro MCP endpoint and connection instructions").action((A,t)=>{let r=t.optsWithGlobals();v({ok:!0,data:{name:q,transport:"http",url:P,docs:FA,install:{claudeCode:`claude mcp add --transport http ${q} ${P}`}}},r)})}import"commander";function SA(e){e.command("install").description("Print the one-line command to add Tiro MCP to Claude Code").option("--print","Print the raw `claude mcp add ...` command to stdout (default behavior)").action((A,t)=>{let r=t.optsWithGlobals(),n=`claude mcp add --transport http ${q} ${P}`;if(r.json){v({ok:!0,data:{command:n,name:q,url:P}},r);return}process.stdout.isTTY&&A.print!==!0&&process.stderr.write(`Run this in your terminal to register Tiro MCP with Claude Code:
|
|
304
|
+
|
|
305
|
+
`),process.stdout.write(`${n}
|
|
306
|
+
`)})}var P="https://mcp.tiro.ooo/mcp",q="tiro",FA="https://api-docs.tiro.ooo/mcp";function DA(e){let A=e.command("mcp").description("Connect Tiro MCP from agent clients (Claude Code, etc.)");XA(A),SA(A)}import Gr from"update-notifier";import{readFile as Vr}from"fs/promises";import{fileURLToPath as Ur}from"url";import{dirname as jr,resolve as PA}from"path";var qA=jr(Ur(import.meta.url)),Qr=[PA(qA,"../../package.json"),PA(qA,"../../../package.json")],zr=1440*60*1e3;async function HA(){if(process.env.NO_UPDATE_NOTIFIER==="1"||process.env.CI||process.stdout.isTTY!==!0)return null;let e=await Yr();if(!e)return null;try{return Gr({pkg:e,updateCheckInterval:zr,shouldNotifyInNpmScript:!1})}catch{return null}}function WA(e){e&&e.update&&e.notify({isGlobal:!0,defer:!1,message:`Update available {currentVersion} \u2192 {latestVersion}
|
|
307
|
+
Run npm install -g {packageName} to update
|
|
2022
308
|
|
|
2023
|
-
|
|
2024
|
-
var EXAMPLES = `
|
|
309
|
+
Changelog: https://www.npmjs.com/package/{packageName}?activeTab=versions`})}async function Yr(){for(let e of Qr)try{let A=await Vr(e,"utf8"),t=JSON.parse(A);if(typeof t.name=="string"&&typeof t.version=="string"&&t.name.length>0&&t.version.length>0)return{name:t.name,version:t.version}}catch{}return null}var Zr=`
|
|
2025
310
|
EXAMPLES
|
|
2026
311
|
$ tiro auth login
|
|
2027
312
|
$ tiro notes list --since 7d
|
|
@@ -2029,6 +314,9 @@ EXAMPLES
|
|
|
2029
314
|
$ tiro notes get <guid> --output ./meeting.md --include transcript
|
|
2030
315
|
$ tiro notes transcript <guid> --format md --output ./transcript.md
|
|
2031
316
|
$ tiro notes transcript <guid> --format md --no-timestamps --output ./clean.md
|
|
317
|
+
$ tiro folders list --workspace <guid>
|
|
318
|
+
$ tiro wiki search "onboarding" --json
|
|
319
|
+
$ tiro wiki graph <pageGuid> --mode around --radius 2
|
|
2032
320
|
$ tiro mcp install # one-line setup for Claude Code
|
|
2033
321
|
|
|
2034
322
|
ENVIRONMENT
|
|
@@ -2039,59 +327,9 @@ ENVIRONMENT
|
|
|
2039
327
|
|
|
2040
328
|
DOCS
|
|
2041
329
|
https://api-docs.tiro.ooo/cli
|
|
2042
|
-
`;
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
registerNotes(program);
|
|
2049
|
-
registerMcp(program);
|
|
2050
|
-
return program;
|
|
2051
|
-
}
|
|
2052
|
-
async function main() {
|
|
2053
|
-
const program = buildProgram();
|
|
2054
|
-
const notifier = await startUpdateCheck();
|
|
2055
|
-
try {
|
|
2056
|
-
await program.parseAsync(process.argv);
|
|
2057
|
-
emitUpdateBanner(notifier);
|
|
2058
|
-
} catch (err) {
|
|
2059
|
-
handleError(err, program);
|
|
2060
|
-
}
|
|
2061
|
-
}
|
|
2062
|
-
function handleError(err, program) {
|
|
2063
|
-
const opts = program.opts();
|
|
2064
|
-
if (err instanceof TiroError) {
|
|
2065
|
-
if (opts.json) {
|
|
2066
|
-
printError(err.toJSON());
|
|
2067
|
-
} else if (!opts.quiet) {
|
|
2068
|
-
process.stderr.write(`${color("\u2717", "red", opts)} ${err.message}
|
|
2069
|
-
`);
|
|
2070
|
-
if (err.suggestion) {
|
|
2071
|
-
process.stderr.write(` ${color("\u2192", "gray", opts)} ${err.suggestion}
|
|
2072
|
-
`);
|
|
2073
|
-
}
|
|
2074
|
-
}
|
|
2075
|
-
process.exit(err.exitCode);
|
|
2076
|
-
}
|
|
2077
|
-
if (err instanceof Error) {
|
|
2078
|
-
if (opts.json) {
|
|
2079
|
-
printError({
|
|
2080
|
-
ok: false,
|
|
2081
|
-
error: { code: "internal_error", message: err.message, errorType: "internal_error" }
|
|
2082
|
-
});
|
|
2083
|
-
} else if (!opts.quiet) {
|
|
2084
|
-
process.stderr.write(`${color("\u2717", "red", opts)} ${err.message}
|
|
2085
|
-
`);
|
|
2086
|
-
}
|
|
2087
|
-
process.exit(ExitCode.Generic);
|
|
2088
|
-
}
|
|
2089
|
-
process.stderr.write(`Unknown error: ${String(err)}
|
|
2090
|
-
`);
|
|
2091
|
-
process.exit(ExitCode.Generic);
|
|
2092
|
-
}
|
|
2093
|
-
main().catch((err) => {
|
|
2094
|
-
process.stderr.write(`Fatal: ${String(err)}
|
|
2095
|
-
`);
|
|
2096
|
-
process.exit(ExitCode.Generic);
|
|
2097
|
-
});
|
|
330
|
+
`;function Jr(){let e=new Kr;return e.name("tiro").description("Tiro AI notes & transcripts \u2014 agent-first command line").version(fe,"-v, --version","Print version").option("--hostname <url>","API base URL (default: https://api.tiro.ooo)").option("--json","Force JSON output").option("--pretty","Force pretty (human) output").option("--quiet","Suppress non-error output").option("--verbose","Verbose logging to stderr").option("--no-color","Disable ANSI colors").addHelpText("after",Zr),e.showHelpAfterError("(run `tiro --help` for available commands)"),_e(e),mA(e),wA(e),kA(e),OA(e),DA(e),e}async function $r(){let e=Jr(),A=await HA();try{await e.parseAsync(process.argv),WA(A)}catch(t){_r(t,e)}}function _r(e,A){let t=A.opts();e instanceof f&&(t.json?te(e.toJSON()):t.quiet||(process.stderr.write(`${a("\u2717","red",t)} ${e.message}
|
|
331
|
+
`),e.suggestion&&process.stderr.write(` ${a("\u2192","gray",t)} ${e.suggestion}
|
|
332
|
+
`)),process.exit(e.exitCode)),e instanceof Error&&(t.json?te({ok:!1,error:{code:"internal_error",message:e.message,errorType:"internal_error"}}):t.quiet||process.stderr.write(`${a("\u2717","red",t)} ${e.message}
|
|
333
|
+
`),process.exit(u.Generic)),process.stderr.write(`Unknown error: ${String(e)}
|
|
334
|
+
`),process.exit(u.Generic)}$r().catch(e=>{process.stderr.write(`Fatal: ${String(e)}
|
|
335
|
+
`),process.exit(u.Generic)});
|