letthemquibble 1.1.0 → 1.2.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 +55 -14
- package/dist/quibble.js +801 -271
- package/package.json +2 -1
package/dist/quibble.js
CHANGED
|
@@ -51,11 +51,11 @@ var require_error = __commonJS({
|
|
|
51
51
|
* @param {string} code an id string representing the error
|
|
52
52
|
* @param {string} message human-readable description of the error
|
|
53
53
|
*/
|
|
54
|
-
constructor(exitCode,
|
|
54
|
+
constructor(exitCode, code2, message) {
|
|
55
55
|
super(message);
|
|
56
56
|
Error.captureStackTrace(this, this.constructor);
|
|
57
57
|
this.name = this.constructor.name;
|
|
58
|
-
this.code =
|
|
58
|
+
this.code = code2;
|
|
59
59
|
this.exitCode = exitCode;
|
|
60
60
|
this.nestedError = void 0;
|
|
61
61
|
}
|
|
@@ -1649,9 +1649,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1649
1649
|
* @return never
|
|
1650
1650
|
* @private
|
|
1651
1651
|
*/
|
|
1652
|
-
_exit(exitCode,
|
|
1652
|
+
_exit(exitCode, code2, message) {
|
|
1653
1653
|
if (this._exitCallback) {
|
|
1654
|
-
this._exitCallback(new CommanderError2(exitCode,
|
|
1654
|
+
this._exitCallback(new CommanderError2(exitCode, code2, message));
|
|
1655
1655
|
}
|
|
1656
1656
|
process3.exit(exitCode);
|
|
1657
1657
|
}
|
|
@@ -2289,14 +2289,14 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2289
2289
|
});
|
|
2290
2290
|
}
|
|
2291
2291
|
const exitCallback = this._exitCallback;
|
|
2292
|
-
proc.on("close", (
|
|
2293
|
-
|
|
2292
|
+
proc.on("close", (code2) => {
|
|
2293
|
+
code2 = code2 ?? 1;
|
|
2294
2294
|
if (!exitCallback) {
|
|
2295
|
-
process3.exit(
|
|
2295
|
+
process3.exit(code2);
|
|
2296
2296
|
} else {
|
|
2297
2297
|
exitCallback(
|
|
2298
2298
|
new CommanderError2(
|
|
2299
|
-
|
|
2299
|
+
code2,
|
|
2300
2300
|
"commander.executeSubCommandAsync",
|
|
2301
2301
|
"(close)"
|
|
2302
2302
|
)
|
|
@@ -2803,8 +2803,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2803
2803
|
}
|
|
2804
2804
|
const config2 = errorOptions || {};
|
|
2805
2805
|
const exitCode = config2.exitCode || 1;
|
|
2806
|
-
const
|
|
2807
|
-
this._exit(exitCode,
|
|
2806
|
+
const code2 = config2.code || "commander.error";
|
|
2807
|
+
this._exit(exitCode, code2, message);
|
|
2808
2808
|
}
|
|
2809
2809
|
/**
|
|
2810
2810
|
* Apply any option related environment variables, if option does
|
|
@@ -6578,12 +6578,12 @@ function isIso7064Mod97(iban3) {
|
|
|
6578
6578
|
let remainder = 0;
|
|
6579
6579
|
const len = iban3.length;
|
|
6580
6580
|
for (let i = 4; i < len; i++) {
|
|
6581
|
-
const
|
|
6582
|
-
remainder = (
|
|
6581
|
+
const code2 = iban3.charCodeAt(i);
|
|
6582
|
+
remainder = (code2 >= 65 ? remainder * 100 + (code2 - 55) : remainder * 10 + (code2 - 48)) % 97;
|
|
6583
6583
|
}
|
|
6584
6584
|
for (let i = 0; i < 4; i++) {
|
|
6585
|
-
const
|
|
6586
|
-
remainder = (
|
|
6585
|
+
const code2 = iban3.charCodeAt(i);
|
|
6586
|
+
remainder = (code2 >= 65 ? remainder * 100 + (code2 - 55) : remainder * 10 + (code2 - 48)) % 97;
|
|
6587
6587
|
}
|
|
6588
6588
|
return remainder === 1;
|
|
6589
6589
|
}
|
|
@@ -7013,7 +7013,7 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
|
|
|
7013
7013
|
}
|
|
7014
7014
|
return propValues;
|
|
7015
7015
|
});
|
|
7016
|
-
const
|
|
7016
|
+
const isObject3 = isObject;
|
|
7017
7017
|
const catchall = def.catchall;
|
|
7018
7018
|
let value;
|
|
7019
7019
|
const memo2 = globalConfig.memoizer;
|
|
@@ -7021,7 +7021,7 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
|
|
|
7021
7021
|
inst._zod.parse = (payload, ctx) => {
|
|
7022
7022
|
value ?? (value = _normalized.value);
|
|
7023
7023
|
const input2 = payload.value;
|
|
7024
|
-
if (!
|
|
7024
|
+
if (!isObject3(input2)) {
|
|
7025
7025
|
payload.issues.push({
|
|
7026
7026
|
expected: "object",
|
|
7027
7027
|
code: "invalid_type",
|
|
@@ -7157,7 +7157,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
7157
7157
|
return doc.compile();
|
|
7158
7158
|
};
|
|
7159
7159
|
let fastpass;
|
|
7160
|
-
const
|
|
7160
|
+
const isObject3 = isObject;
|
|
7161
7161
|
const jit = !globalConfig.jitless;
|
|
7162
7162
|
const allowsEval2 = allowsEval;
|
|
7163
7163
|
const fastEnabled = jit && allowsEval2.value;
|
|
@@ -7166,7 +7166,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
7166
7166
|
inst._zod.parse = (payload, ctx) => {
|
|
7167
7167
|
value ?? (value = _normalized.value);
|
|
7168
7168
|
const input2 = payload.value;
|
|
7169
|
-
if (!
|
|
7169
|
+
if (!isObject3(input2)) {
|
|
7170
7170
|
payload.issues.push({
|
|
7171
7171
|
expected: "object",
|
|
7172
7172
|
code: "invalid_type",
|
|
@@ -16501,12 +16501,12 @@ function compileFn(schema, options) {
|
|
|
16501
16501
|
doc.write(outputAccessor === null ? `return true;` : `return ${outputAccessor};`);
|
|
16502
16502
|
const constantNames = ["INVALID", ...ctx.constants.keys()];
|
|
16503
16503
|
const constantValues = [INVALID, ...ctx.constants.values()];
|
|
16504
|
-
const
|
|
16504
|
+
const code2 = doc.content.join("\n");
|
|
16505
16505
|
const fullCode = options?.debug ? constantNames.length > 0 ? `// Constants: ${constantNames.join(", ")}
|
|
16506
|
-
${
|
|
16506
|
+
${code2}` : code2 : "";
|
|
16507
16507
|
const F = Function;
|
|
16508
16508
|
const factoryCode = `return (input) => {
|
|
16509
|
-
${
|
|
16509
|
+
${code2}
|
|
16510
16510
|
}`;
|
|
16511
16511
|
let fn;
|
|
16512
16512
|
try {
|
|
@@ -19432,8 +19432,8 @@ function finalize(ctx, schema) {
|
|
|
19432
19432
|
compactTypeUnion(entry[1].def ?? entry[1].schema);
|
|
19433
19433
|
}
|
|
19434
19434
|
}
|
|
19435
|
-
for (const
|
|
19436
|
-
|
|
19435
|
+
for (const rewrite2 of ctx.deferred)
|
|
19436
|
+
rewrite2();
|
|
19437
19437
|
if (ctx.intersections.length) {
|
|
19438
19438
|
const carriers = /* @__PURE__ */ new Map();
|
|
19439
19439
|
for (const seen of ctx.seen.values()) {
|
|
@@ -23222,7 +23222,7 @@ var RESERVED_SLUGS = [
|
|
|
23222
23222
|
];
|
|
23223
23223
|
var LOCAL_ORG = "local";
|
|
23224
23224
|
var DISPLAY_NAME_MAX = 64;
|
|
23225
|
-
var PROJECT_NAME_RE = /^[
|
|
23225
|
+
var PROJECT_NAME_RE = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
23226
23226
|
var DEFAULT_LIMIT = 50;
|
|
23227
23227
|
var MAX_LIMIT = 200;
|
|
23228
23228
|
var DEFAULT_SEVERITY = "medium";
|
|
@@ -23238,9 +23238,9 @@ var IDEMPOTENCY_KEY_MAX = 128;
|
|
|
23238
23238
|
var QuibbleError = class extends Error {
|
|
23239
23239
|
/** Stable machine-readable code, also used by the API error body. */
|
|
23240
23240
|
code;
|
|
23241
|
-
constructor(
|
|
23241
|
+
constructor(code2, message, options) {
|
|
23242
23242
|
super(message, options);
|
|
23243
|
-
this.code =
|
|
23243
|
+
this.code = code2;
|
|
23244
23244
|
this.name = new.target.name;
|
|
23245
23245
|
}
|
|
23246
23246
|
};
|
|
@@ -23476,7 +23476,10 @@ var timestampSchema = external_exports.string({ message: TIMESTAMP_MESSAGE }).re
|
|
|
23476
23476
|
function toMilliseconds(timestamp) {
|
|
23477
23477
|
return new Date(timestamp).toISOString();
|
|
23478
23478
|
}
|
|
23479
|
-
|
|
23479
|
+
function foldProjectName(name) {
|
|
23480
|
+
return name.replace(/[A-Z]+/g, (letters) => letters.toLowerCase());
|
|
23481
|
+
}
|
|
23482
|
+
var projectNameSchema = external_exports.string({ message: PROJECT_MESSAGE }).overwrite(foldProjectName).regex(PROJECT_NAME_RE, { message: PROJECT_MESSAGE });
|
|
23480
23483
|
var orgSlugSchema = external_exports.string({ message: SLUG_MESSAGE }).refine((value) => ORG_SLUG_RE.test(value) && !value.includes("--"), {
|
|
23481
23484
|
message: SLUG_MESSAGE
|
|
23482
23485
|
});
|
|
@@ -24046,6 +24049,34 @@ function listQueryOf(filter) {
|
|
|
24046
24049
|
return pairs;
|
|
24047
24050
|
}
|
|
24048
24051
|
|
|
24052
|
+
// ../core/dist/endpoint.js
|
|
24053
|
+
var DEFAULT_ENDPOINT = "https://api.letthemquibble.com";
|
|
24054
|
+
var DISCOVERY_PATH = "/.well-known/quibble.json";
|
|
24055
|
+
var LOCAL_ENDPOINT = "local";
|
|
24056
|
+
var LOOPBACK_HOSTS = ["localhost", "127.0.0.1"];
|
|
24057
|
+
function trimSlash(url2) {
|
|
24058
|
+
return url2.replace(/\/+$/, "");
|
|
24059
|
+
}
|
|
24060
|
+
function normalizeEndpoint(url2) {
|
|
24061
|
+
const text = trimSlash(url2.trim());
|
|
24062
|
+
try {
|
|
24063
|
+
const parsed = new URL(text);
|
|
24064
|
+
return `${parsed.protocol}//${parsed.host}${trimSlash(parsed.pathname)}`;
|
|
24065
|
+
} catch {
|
|
24066
|
+
return text;
|
|
24067
|
+
}
|
|
24068
|
+
}
|
|
24069
|
+
function isDefaultEndpoint(url2) {
|
|
24070
|
+
return normalizeEndpoint(url2) === DEFAULT_ENDPOINT;
|
|
24071
|
+
}
|
|
24072
|
+
function isLoopbackUrl(url2) {
|
|
24073
|
+
try {
|
|
24074
|
+
return LOOPBACK_HOSTS.includes(new URL(url2).hostname);
|
|
24075
|
+
} catch {
|
|
24076
|
+
return false;
|
|
24077
|
+
}
|
|
24078
|
+
}
|
|
24079
|
+
|
|
24049
24080
|
// ../core/dist/service.js
|
|
24050
24081
|
import { createHash, randomUUID } from "node:crypto";
|
|
24051
24082
|
function applyTagRules(tags, allowedTags) {
|
|
@@ -24459,7 +24490,7 @@ function defaultConfigPath(env = process.env) {
|
|
|
24459
24490
|
}
|
|
24460
24491
|
|
|
24461
24492
|
// src/config.ts
|
|
24462
|
-
import { readFileSync as readFileSync2 } from "node:fs";
|
|
24493
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
24463
24494
|
import { dirname as dirname3, isAbsolute, join as join3, relative, resolve, sep } from "node:path";
|
|
24464
24495
|
|
|
24465
24496
|
// src/credentials.ts
|
|
@@ -24473,6 +24504,92 @@ import {
|
|
|
24473
24504
|
writeFileSync
|
|
24474
24505
|
} from "node:fs";
|
|
24475
24506
|
import { dirname as dirname2, join as join2 } from "node:path";
|
|
24507
|
+
var CREDENTIALS_FILENAME = "credentials.json";
|
|
24508
|
+
var CREDENTIALS_MODE = 384;
|
|
24509
|
+
var HOME_MODE = 448;
|
|
24510
|
+
function defaultCredentialsPath(env) {
|
|
24511
|
+
return join2(defaultQuibbleHome(env), CREDENTIALS_FILENAME);
|
|
24512
|
+
}
|
|
24513
|
+
function readEntry(value) {
|
|
24514
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
24515
|
+
return void 0;
|
|
24516
|
+
}
|
|
24517
|
+
const raw = value;
|
|
24518
|
+
const keys = ["endpoint", "refreshToken", "accessToken", "expiresAt", "sub", "email"];
|
|
24519
|
+
for (const key of keys) {
|
|
24520
|
+
if (typeof raw[key] !== "string" || raw[key] === "") return void 0;
|
|
24521
|
+
}
|
|
24522
|
+
return {
|
|
24523
|
+
endpoint: raw["endpoint"],
|
|
24524
|
+
refreshToken: raw["refreshToken"],
|
|
24525
|
+
accessToken: raw["accessToken"],
|
|
24526
|
+
expiresAt: raw["expiresAt"],
|
|
24527
|
+
sub: raw["sub"],
|
|
24528
|
+
email: raw["email"]
|
|
24529
|
+
};
|
|
24530
|
+
}
|
|
24531
|
+
function readCredentials(env) {
|
|
24532
|
+
const path = defaultCredentialsPath(env);
|
|
24533
|
+
let text;
|
|
24534
|
+
try {
|
|
24535
|
+
text = readFileSync(path, "utf8");
|
|
24536
|
+
} catch (error62) {
|
|
24537
|
+
if (isNotFound(error62)) return {};
|
|
24538
|
+
throw new ConfigError(`cannot read ${path}`, path, { cause: error62 });
|
|
24539
|
+
}
|
|
24540
|
+
let parsed;
|
|
24541
|
+
try {
|
|
24542
|
+
parsed = JSON.parse(text);
|
|
24543
|
+
} catch (error62) {
|
|
24544
|
+
throw new ConfigError(`invalid JSON in ${path}`, path, { cause: error62 });
|
|
24545
|
+
}
|
|
24546
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
24547
|
+
throw new ConfigError(`invalid credentials in ${path}: must be a JSON object`, path);
|
|
24548
|
+
}
|
|
24549
|
+
const entries = {};
|
|
24550
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
24551
|
+
const entry = readEntry(value);
|
|
24552
|
+
if (entry !== void 0) entries[key] = entry;
|
|
24553
|
+
}
|
|
24554
|
+
return entries;
|
|
24555
|
+
}
|
|
24556
|
+
function findCredential(env, endpoint) {
|
|
24557
|
+
return readCredentials(env)[normalizeEndpoint(endpoint)];
|
|
24558
|
+
}
|
|
24559
|
+
function writeCredentials(env, entries) {
|
|
24560
|
+
const path = defaultCredentialsPath(env);
|
|
24561
|
+
mkdirSync(dirname2(path), { recursive: true, mode: HOME_MODE });
|
|
24562
|
+
const temp = join2(
|
|
24563
|
+
dirname2(path),
|
|
24564
|
+
`.${CREDENTIALS_FILENAME}.${randomBytes(6).toString("hex")}`
|
|
24565
|
+
);
|
|
24566
|
+
try {
|
|
24567
|
+
writeFileSync(temp, `${JSON.stringify(entries, null, 2)}
|
|
24568
|
+
`, {
|
|
24569
|
+
encoding: "utf8",
|
|
24570
|
+
mode: CREDENTIALS_MODE
|
|
24571
|
+
});
|
|
24572
|
+
chmodSync(temp, CREDENTIALS_MODE);
|
|
24573
|
+
renameSync(temp, path);
|
|
24574
|
+
} catch (error62) {
|
|
24575
|
+
rmSync(temp, { force: true });
|
|
24576
|
+
throw error62;
|
|
24577
|
+
}
|
|
24578
|
+
}
|
|
24579
|
+
function saveCredential(env, credential) {
|
|
24580
|
+
const key = normalizeEndpoint(credential.endpoint);
|
|
24581
|
+
const stored = { ...credential, endpoint: key };
|
|
24582
|
+
writeCredentials(env, { ...readCredentials(env), [key]: stored });
|
|
24583
|
+
return stored;
|
|
24584
|
+
}
|
|
24585
|
+
function removeCredential(env, endpoint) {
|
|
24586
|
+
const entries = readCredentials(env);
|
|
24587
|
+
const key = normalizeEndpoint(endpoint);
|
|
24588
|
+
if (entries[key] === void 0) return false;
|
|
24589
|
+
delete entries[key];
|
|
24590
|
+
writeCredentials(env, entries);
|
|
24591
|
+
return true;
|
|
24592
|
+
}
|
|
24476
24593
|
|
|
24477
24594
|
// src/errors.ts
|
|
24478
24595
|
var AMBIGUOUS_ID = "ambiguous_id";
|
|
@@ -24525,7 +24642,7 @@ var DEBUG_ENV = "QUIBBLE_DEBUG";
|
|
|
24525
24642
|
function debugEnabled(env) {
|
|
24526
24643
|
return env[DEBUG_ENV] === "1";
|
|
24527
24644
|
}
|
|
24528
|
-
function
|
|
24645
|
+
function trimSlash2(url2) {
|
|
24529
24646
|
return url2.replace(/\/+$/, "");
|
|
24530
24647
|
}
|
|
24531
24648
|
function originOf(url2) {
|
|
@@ -24629,103 +24746,6 @@ function remoteFailure(site, response, text) {
|
|
|
24629
24746
|
});
|
|
24630
24747
|
}
|
|
24631
24748
|
|
|
24632
|
-
// src/credentials.ts
|
|
24633
|
-
var CREDENTIALS_FILENAME = "credentials.json";
|
|
24634
|
-
var CREDENTIALS_MODE = 384;
|
|
24635
|
-
var HOME_MODE = 448;
|
|
24636
|
-
function defaultCredentialsPath(env) {
|
|
24637
|
-
return join2(defaultQuibbleHome(env), CREDENTIALS_FILENAME);
|
|
24638
|
-
}
|
|
24639
|
-
function normalizeEndpoint(endpoint) {
|
|
24640
|
-
const text = trimSlash(endpoint.trim());
|
|
24641
|
-
try {
|
|
24642
|
-
const url2 = new URL(text);
|
|
24643
|
-
return `${url2.protocol}//${url2.host}${trimSlash(url2.pathname)}`;
|
|
24644
|
-
} catch {
|
|
24645
|
-
return text;
|
|
24646
|
-
}
|
|
24647
|
-
}
|
|
24648
|
-
function readEntry(value) {
|
|
24649
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
24650
|
-
return void 0;
|
|
24651
|
-
}
|
|
24652
|
-
const raw = value;
|
|
24653
|
-
const keys = ["endpoint", "refreshToken", "accessToken", "expiresAt", "sub", "email"];
|
|
24654
|
-
for (const key of keys) {
|
|
24655
|
-
if (typeof raw[key] !== "string" || raw[key] === "") return void 0;
|
|
24656
|
-
}
|
|
24657
|
-
return {
|
|
24658
|
-
endpoint: raw["endpoint"],
|
|
24659
|
-
refreshToken: raw["refreshToken"],
|
|
24660
|
-
accessToken: raw["accessToken"],
|
|
24661
|
-
expiresAt: raw["expiresAt"],
|
|
24662
|
-
sub: raw["sub"],
|
|
24663
|
-
email: raw["email"]
|
|
24664
|
-
};
|
|
24665
|
-
}
|
|
24666
|
-
function readCredentials(env) {
|
|
24667
|
-
const path = defaultCredentialsPath(env);
|
|
24668
|
-
let text;
|
|
24669
|
-
try {
|
|
24670
|
-
text = readFileSync(path, "utf8");
|
|
24671
|
-
} catch (error62) {
|
|
24672
|
-
if (isNotFound(error62)) return {};
|
|
24673
|
-
throw new ConfigError(`cannot read ${path}`, path, { cause: error62 });
|
|
24674
|
-
}
|
|
24675
|
-
let parsed;
|
|
24676
|
-
try {
|
|
24677
|
-
parsed = JSON.parse(text);
|
|
24678
|
-
} catch (error62) {
|
|
24679
|
-
throw new ConfigError(`invalid JSON in ${path}`, path, { cause: error62 });
|
|
24680
|
-
}
|
|
24681
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
24682
|
-
throw new ConfigError(`invalid credentials in ${path}: must be a JSON object`, path);
|
|
24683
|
-
}
|
|
24684
|
-
const entries = {};
|
|
24685
|
-
for (const [key, value] of Object.entries(parsed)) {
|
|
24686
|
-
const entry = readEntry(value);
|
|
24687
|
-
if (entry !== void 0) entries[key] = entry;
|
|
24688
|
-
}
|
|
24689
|
-
return entries;
|
|
24690
|
-
}
|
|
24691
|
-
function findCredential(env, endpoint) {
|
|
24692
|
-
return readCredentials(env)[normalizeEndpoint(endpoint)];
|
|
24693
|
-
}
|
|
24694
|
-
function writeCredentials(env, entries) {
|
|
24695
|
-
const path = defaultCredentialsPath(env);
|
|
24696
|
-
mkdirSync(dirname2(path), { recursive: true, mode: HOME_MODE });
|
|
24697
|
-
const temp = join2(
|
|
24698
|
-
dirname2(path),
|
|
24699
|
-
`.${CREDENTIALS_FILENAME}.${randomBytes(6).toString("hex")}`
|
|
24700
|
-
);
|
|
24701
|
-
try {
|
|
24702
|
-
writeFileSync(temp, `${JSON.stringify(entries, null, 2)}
|
|
24703
|
-
`, {
|
|
24704
|
-
encoding: "utf8",
|
|
24705
|
-
mode: CREDENTIALS_MODE
|
|
24706
|
-
});
|
|
24707
|
-
chmodSync(temp, CREDENTIALS_MODE);
|
|
24708
|
-
renameSync(temp, path);
|
|
24709
|
-
} catch (error62) {
|
|
24710
|
-
rmSync(temp, { force: true });
|
|
24711
|
-
throw error62;
|
|
24712
|
-
}
|
|
24713
|
-
}
|
|
24714
|
-
function saveCredential(env, credential) {
|
|
24715
|
-
const key = normalizeEndpoint(credential.endpoint);
|
|
24716
|
-
const stored = { ...credential, endpoint: key };
|
|
24717
|
-
writeCredentials(env, { ...readCredentials(env), [key]: stored });
|
|
24718
|
-
return stored;
|
|
24719
|
-
}
|
|
24720
|
-
function removeCredential(env, endpoint) {
|
|
24721
|
-
const entries = readCredentials(env);
|
|
24722
|
-
const key = normalizeEndpoint(endpoint);
|
|
24723
|
-
if (entries[key] === void 0) return false;
|
|
24724
|
-
delete entries[key];
|
|
24725
|
-
writeCredentials(env, entries);
|
|
24726
|
-
return true;
|
|
24727
|
-
}
|
|
24728
|
-
|
|
24729
24749
|
// src/cognito.ts
|
|
24730
24750
|
var TARGET_PREFIX = "AWSCognitoIdentityProviderService.";
|
|
24731
24751
|
var CONTENT_TYPE = "application/x-amz-json-1.1";
|
|
@@ -24925,9 +24945,8 @@ function actAsRefused(sub, endpoint) {
|
|
|
24925
24945
|
function clockOf(options) {
|
|
24926
24946
|
return options.now ?? (() => /* @__PURE__ */ new Date());
|
|
24927
24947
|
}
|
|
24928
|
-
var LOCAL_HOSTS = ["localhost", "127.0.0.1"];
|
|
24929
24948
|
async function fetchAuthConfig(endpoint, options = {}) {
|
|
24930
|
-
const url2 = `${
|
|
24949
|
+
const url2 = `${trimSlash2(endpoint)}${AUTH_CONFIG_PATH}`;
|
|
24931
24950
|
const site = { method: "GET", url: url2, debug: options.debug };
|
|
24932
24951
|
const response = await send(url2, { method: "GET", ...options });
|
|
24933
24952
|
const text = await readText(response, site);
|
|
@@ -24956,13 +24975,6 @@ function bearerAuthorizer(token) {
|
|
|
24956
24975
|
};
|
|
24957
24976
|
}
|
|
24958
24977
|
var DEV_TOKEN = "dev";
|
|
24959
|
-
function isLoopback(endpoint) {
|
|
24960
|
-
try {
|
|
24961
|
-
return LOCAL_HOSTS.includes(new URL(endpoint).hostname);
|
|
24962
|
-
} catch {
|
|
24963
|
-
return false;
|
|
24964
|
-
}
|
|
24965
|
-
}
|
|
24966
24978
|
var DevAuthorizer = class {
|
|
24967
24979
|
actAs;
|
|
24968
24980
|
#endpoint;
|
|
@@ -25067,12 +25079,97 @@ function createAuthorizer(endpoint, options) {
|
|
|
25067
25079
|
return new SessionAuthorizer(endpoint, options);
|
|
25068
25080
|
}
|
|
25069
25081
|
|
|
25082
|
+
// src/endpoint.ts
|
|
25083
|
+
var DISCOVERY_TIMEOUT_MS = 5e3;
|
|
25084
|
+
var SCHEMES = ["http:", "https:"];
|
|
25085
|
+
function parseEndpoint(value, from) {
|
|
25086
|
+
const text = value.trim();
|
|
25087
|
+
if (text.toLowerCase() === LOCAL_ENDPOINT) return { kind: "local" };
|
|
25088
|
+
const invalid = new ConfigError(
|
|
25089
|
+
`invalid endpoint "${text}" from ${from}: pass an https URL, a host, or local`
|
|
25090
|
+
);
|
|
25091
|
+
let url2 = text;
|
|
25092
|
+
if (!text.includes("://")) {
|
|
25093
|
+
url2 = isLoopbackUrl(`http://${text}`) ? `http://${text}` : `https://${text}`;
|
|
25094
|
+
}
|
|
25095
|
+
let parsed;
|
|
25096
|
+
try {
|
|
25097
|
+
parsed = new URL(url2);
|
|
25098
|
+
} catch {
|
|
25099
|
+
throw invalid;
|
|
25100
|
+
}
|
|
25101
|
+
if (!SCHEMES.includes(parsed.protocol) || parsed.hostname === "") throw invalid;
|
|
25102
|
+
return { kind: "url", url: normalizeEndpoint(url2) };
|
|
25103
|
+
}
|
|
25104
|
+
var discoveries = /* @__PURE__ */ new Map();
|
|
25105
|
+
function resetDiscovery() {
|
|
25106
|
+
discoveries.clear();
|
|
25107
|
+
}
|
|
25108
|
+
function discoverApiUrl(url2, options = {}) {
|
|
25109
|
+
const key = normalizeEndpoint(url2);
|
|
25110
|
+
if (isDefaultEndpoint(key)) return Promise.resolve(DEFAULT_ENDPOINT);
|
|
25111
|
+
const known = discoveries.get(key);
|
|
25112
|
+
if (known !== void 0) return known;
|
|
25113
|
+
const found = lookup(key, options).then((api) => {
|
|
25114
|
+
if (!discoveries.has(api)) discoveries.set(api, Promise.resolve(api));
|
|
25115
|
+
return api;
|
|
25116
|
+
});
|
|
25117
|
+
discoveries.set(key, found);
|
|
25118
|
+
return found;
|
|
25119
|
+
}
|
|
25120
|
+
async function lookup(url2, options) {
|
|
25121
|
+
const target = `${url2}${DISCOVERY_PATH}`;
|
|
25122
|
+
try {
|
|
25123
|
+
const response = await send(target, {
|
|
25124
|
+
method: "GET",
|
|
25125
|
+
headers: { accept: "application/json" },
|
|
25126
|
+
fetch: options.fetch,
|
|
25127
|
+
debug: options.debug,
|
|
25128
|
+
timeoutMs: options.timeoutMs ?? DISCOVERY_TIMEOUT_MS
|
|
25129
|
+
});
|
|
25130
|
+
const text = await readText(response, { method: "GET", url: target });
|
|
25131
|
+
if (!response.ok) return url2;
|
|
25132
|
+
const api = stringOf(asRecord(parseQuietly(text)), "api");
|
|
25133
|
+
if (api === void 0) return url2;
|
|
25134
|
+
const parsed = new URL(api.trim());
|
|
25135
|
+
if (!SCHEMES.includes(parsed.protocol)) return url2;
|
|
25136
|
+
return normalizeEndpoint(api);
|
|
25137
|
+
} catch {
|
|
25138
|
+
return url2;
|
|
25139
|
+
}
|
|
25140
|
+
}
|
|
25141
|
+
|
|
25070
25142
|
// src/config.ts
|
|
25071
|
-
var NO_ENDPOINT_MESSAGE = "no endpoint: pass --endpoint <url>, set QUIBBLE_ENDPOINT, or add one to .quibblerc";
|
|
25072
25143
|
function requireEndpoint(context) {
|
|
25073
|
-
if (context.endpoint === void 0)
|
|
25144
|
+
if (context.endpoint === void 0) {
|
|
25145
|
+
throw new ConfigError(`local mode has no endpoint: ${context.localFix ?? ""}`);
|
|
25146
|
+
}
|
|
25074
25147
|
return context.endpoint;
|
|
25075
25148
|
}
|
|
25149
|
+
async function resolveEndpoint(context, io) {
|
|
25150
|
+
const endpoint = requireEndpoint(context);
|
|
25151
|
+
if (context.discovered) return endpoint;
|
|
25152
|
+
const api = await discoverApiUrl(endpoint, {
|
|
25153
|
+
fetch: io.fetch,
|
|
25154
|
+
debug: context.debug,
|
|
25155
|
+
timeoutMs: context.timeoutMs
|
|
25156
|
+
});
|
|
25157
|
+
context.endpoint = api;
|
|
25158
|
+
context.credential = findCredential(io.env, api);
|
|
25159
|
+
context.discovered = true;
|
|
25160
|
+
return api;
|
|
25161
|
+
}
|
|
25162
|
+
function foreignOrgMessage(context) {
|
|
25163
|
+
const slug = context.slug;
|
|
25164
|
+
if (context.mode !== "local" || slug === void 0 || slug === LOCAL_ORG) {
|
|
25165
|
+
return void 0;
|
|
25166
|
+
}
|
|
25167
|
+
return `local mode holds the organization ${LOCAL_ORG} only, not ${slug}: ${context.localFix ?? ""} to file to ${slug} on a server, or pass --org ${LOCAL_ORG}`;
|
|
25168
|
+
}
|
|
25169
|
+
function refuseForeignOrg(context) {
|
|
25170
|
+
const message = foreignOrgMessage(context);
|
|
25171
|
+
if (message !== void 0) throw new ConfigError(message);
|
|
25172
|
+
}
|
|
25076
25173
|
var NO_ORG_MESSAGE = "no organization: pass --org <slug> or run `quibble init --org <slug>`";
|
|
25077
25174
|
function requireOrg(context) {
|
|
25078
25175
|
if (context.org === void 0) throw new ConfigError(NO_ORG_MESSAGE);
|
|
@@ -25196,20 +25293,76 @@ function firstValue(...values) {
|
|
|
25196
25293
|
}
|
|
25197
25294
|
return void 0;
|
|
25198
25295
|
}
|
|
25296
|
+
function localFixOf(candidate) {
|
|
25297
|
+
switch (candidate.source) {
|
|
25298
|
+
case "--endpoint":
|
|
25299
|
+
return "drop --endpoint local";
|
|
25300
|
+
case "QUIBBLE_ENDPOINT":
|
|
25301
|
+
return "unset QUIBBLE_ENDPOINT";
|
|
25302
|
+
default:
|
|
25303
|
+
return `remove "endpoint": "local" from ${candidate.path ?? ""}`;
|
|
25304
|
+
}
|
|
25305
|
+
}
|
|
25306
|
+
function resolveEndpointSource(options, project, user) {
|
|
25307
|
+
if (options.local === true) {
|
|
25308
|
+
return {
|
|
25309
|
+
mode: "local",
|
|
25310
|
+
endpoint: void 0,
|
|
25311
|
+
endpointSource: "--local",
|
|
25312
|
+
localFix: "drop --local"
|
|
25313
|
+
};
|
|
25314
|
+
}
|
|
25315
|
+
const candidates2 = [
|
|
25316
|
+
{ source: "--endpoint", value: options.endpoint },
|
|
25317
|
+
{ source: "QUIBBLE_ENDPOINT", value: options.env["QUIBBLE_ENDPOINT"] },
|
|
25318
|
+
{ source: ".quibblerc", value: project?.endpoint, path: project?.path },
|
|
25319
|
+
{
|
|
25320
|
+
source: "config.json",
|
|
25321
|
+
value: user.endpoint,
|
|
25322
|
+
path: defaultConfigPath(options.env)
|
|
25323
|
+
}
|
|
25324
|
+
];
|
|
25325
|
+
const chosen = candidates2.find(
|
|
25326
|
+
(candidate) => candidate.value !== void 0 && candidate.value.trim() !== ""
|
|
25327
|
+
);
|
|
25328
|
+
if (chosen === void 0) {
|
|
25329
|
+
return {
|
|
25330
|
+
mode: "remote",
|
|
25331
|
+
endpoint: DEFAULT_ENDPOINT,
|
|
25332
|
+
endpointSource: "default",
|
|
25333
|
+
localFix: void 0
|
|
25334
|
+
};
|
|
25335
|
+
}
|
|
25336
|
+
const value = parseEndpoint(chosen.value ?? "", chosen.path ?? chosen.source);
|
|
25337
|
+
if (value.kind === "local") {
|
|
25338
|
+
return {
|
|
25339
|
+
mode: "local",
|
|
25340
|
+
endpoint: void 0,
|
|
25341
|
+
endpointSource: chosen.source,
|
|
25342
|
+
localFix: localFixOf(chosen)
|
|
25343
|
+
};
|
|
25344
|
+
}
|
|
25345
|
+
return {
|
|
25346
|
+
mode: "remote",
|
|
25347
|
+
endpoint: value.url,
|
|
25348
|
+
endpointSource: chosen.source,
|
|
25349
|
+
localFix: void 0
|
|
25350
|
+
};
|
|
25351
|
+
}
|
|
25199
25352
|
function resolveContext(options) {
|
|
25200
25353
|
const project = findProjectConfig(options.cwd);
|
|
25201
25354
|
const user = loadUserConfig(options.env);
|
|
25202
|
-
const endpoint =
|
|
25203
|
-
options
|
|
25204
|
-
|
|
25205
|
-
|
|
25206
|
-
user.endpoint
|
|
25355
|
+
const { mode, endpoint, endpointSource, localFix } = resolveEndpointSource(
|
|
25356
|
+
options,
|
|
25357
|
+
project,
|
|
25358
|
+
user
|
|
25207
25359
|
);
|
|
25208
|
-
const
|
|
25209
|
-
const
|
|
25210
|
-
const
|
|
25360
|
+
const given = firstValue(options.project, project?.project);
|
|
25361
|
+
const name = given === void 0 ? void 0 : foldProjectName(given);
|
|
25362
|
+
const org = firstValue(options.org, project?.org);
|
|
25363
|
+
const slug = org === void 0 ? void 0 : parseOrgSlug(org);
|
|
25211
25364
|
const token = firstValue(options.env[TOKEN_ENV]);
|
|
25212
|
-
const
|
|
25365
|
+
const discovered = endpoint !== void 0 && isDefaultEndpoint(endpoint);
|
|
25213
25366
|
return {
|
|
25214
25367
|
project: name,
|
|
25215
25368
|
org: mode === "local" ? LOCAL_ORG : slug,
|
|
@@ -25219,10 +25372,12 @@ function resolveContext(options) {
|
|
|
25219
25372
|
cwd: project === void 0 ? void 0 : relativeCwd(project.root, options.cwd),
|
|
25220
25373
|
mode,
|
|
25221
25374
|
endpoint,
|
|
25375
|
+
endpointSource,
|
|
25376
|
+
localFix,
|
|
25377
|
+
discovered,
|
|
25378
|
+
timeoutMs: void 0,
|
|
25222
25379
|
token,
|
|
25223
|
-
|
|
25224
|
-
// and no command has to read the file to describe the session.
|
|
25225
|
-
credential: endpoint === void 0 ? void 0 : findCredential(options.env, endpoint),
|
|
25380
|
+
credential: discovered && endpoint !== void 0 ? findCredential(options.env, endpoint) : void 0,
|
|
25226
25381
|
actAs: firstValue(options.env[ACT_AS_ENV]),
|
|
25227
25382
|
debug: debugEnabled(options.env),
|
|
25228
25383
|
agent: firstValue(options.agent, user.agent) ?? DEFAULT_AGENT,
|
|
@@ -25235,6 +25390,15 @@ function resolveContext(options) {
|
|
|
25235
25390
|
projectConfig: project
|
|
25236
25391
|
};
|
|
25237
25392
|
}
|
|
25393
|
+
function rememberEndpoint(env, endpoint) {
|
|
25394
|
+
const path = defaultConfigPath(env);
|
|
25395
|
+
const raw = readJsonFile(path) ?? {};
|
|
25396
|
+
if (stringField(raw, "endpoint", path) !== void 0) return void 0;
|
|
25397
|
+
mkdirSync2(dirname3(path), { recursive: true });
|
|
25398
|
+
writeFileSync2(path, `${JSON.stringify({ ...raw, endpoint }, null, 2)}
|
|
25399
|
+
`, "utf8");
|
|
25400
|
+
return path;
|
|
25401
|
+
}
|
|
25238
25402
|
|
|
25239
25403
|
// src/ids.ts
|
|
25240
25404
|
var FULL_ID_LENGTH = 36;
|
|
@@ -25438,7 +25602,7 @@ var ApiClient = class {
|
|
|
25438
25602
|
constructor(options) {
|
|
25439
25603
|
this.endpoint = options.endpoint;
|
|
25440
25604
|
this.org = options.org;
|
|
25441
|
-
this.#base =
|
|
25605
|
+
this.#base = trimSlash2(options.endpoint);
|
|
25442
25606
|
this.#auth = options.authorizer;
|
|
25443
25607
|
this.#beforeFirst = options.beforeFirst;
|
|
25444
25608
|
this.#http = {
|
|
@@ -25516,6 +25680,13 @@ var ApiClient = class {
|
|
|
25516
25680
|
const retagged = answer2.body["retagged"];
|
|
25517
25681
|
return { ...answer2, retagged: typeof retagged === "number" ? retagged : 0 };
|
|
25518
25682
|
}
|
|
25683
|
+
/**
|
|
25684
|
+
* Soft-deletes a project (section 7.5). The route hides it and its
|
|
25685
|
+
* complaints everywhere and revokes its tokens, and answers `204`.
|
|
25686
|
+
*/
|
|
25687
|
+
async deleteProject(name) {
|
|
25688
|
+
await this.request("DELETE", this.projectPath(name));
|
|
25689
|
+
}
|
|
25519
25690
|
/** Lists the tokens of a project, newest first, as the route sorts them. */
|
|
25520
25691
|
async tokens(project) {
|
|
25521
25692
|
const body = await this.request("GET", `${this.projectPath(project)}/tokens`);
|
|
@@ -25804,7 +25975,7 @@ import {
|
|
|
25804
25975
|
closeSync,
|
|
25805
25976
|
fstatSync,
|
|
25806
25977
|
linkSync,
|
|
25807
|
-
mkdirSync as
|
|
25978
|
+
mkdirSync as mkdirSync3,
|
|
25808
25979
|
openSync,
|
|
25809
25980
|
readFileSync as readFileSync3,
|
|
25810
25981
|
readSync,
|
|
@@ -25878,7 +26049,7 @@ function endsInsideLine(path) {
|
|
|
25878
26049
|
}
|
|
25879
26050
|
function appendLine(path, value) {
|
|
25880
26051
|
try {
|
|
25881
|
-
|
|
26052
|
+
mkdirSync3(dirname4(path), { recursive: true, mode: HOME_MODE });
|
|
25882
26053
|
const lead = endsInsideLine(path) ? "\n" : "";
|
|
25883
26054
|
appendFileSync(path, `${lead}${JSON.stringify(value)}
|
|
25884
26055
|
`, {
|
|
@@ -25996,7 +26167,7 @@ function createLock(lock, owner = { pid: process.pid, host: hostname3(), at: (/*
|
|
|
25996
26167
|
const text = JSON.stringify(owner);
|
|
25997
26168
|
let fd;
|
|
25998
26169
|
try {
|
|
25999
|
-
|
|
26170
|
+
mkdirSync3(dirname4(lock), { recursive: true, mode: HOME_MODE });
|
|
26000
26171
|
fd = openSync(lock, "wx", QUEUE_MODE);
|
|
26001
26172
|
} catch (error62) {
|
|
26002
26173
|
if (error62.code === "EEXIST") return void 0;
|
|
@@ -26046,8 +26217,8 @@ function releaseLock(lock, text) {
|
|
|
26046
26217
|
}
|
|
26047
26218
|
function stopReason(status, text) {
|
|
26048
26219
|
if (status >= 500) return `the API answered ${status}`;
|
|
26049
|
-
const
|
|
26050
|
-
const stops = status === 429 || status === 401 || status === 403 || status === 404 &&
|
|
26220
|
+
const code2 = serverCode(text);
|
|
26221
|
+
const stops = status === 429 || status === 401 || status === 403 || status === 404 && code2 === ORG_NOT_FOUND;
|
|
26051
26222
|
if (!stops) return void 0;
|
|
26052
26223
|
const message = stringOf(asRecord(asRecord(parseQuietly(text))?.["error"]), "message");
|
|
26053
26224
|
return message ?? `the API answered ${status}`;
|
|
@@ -26117,12 +26288,12 @@ async function flushQueue(options) {
|
|
|
26117
26288
|
return result;
|
|
26118
26289
|
}
|
|
26119
26290
|
const record2 = asRecord(asRecord(parseQuietly(text))?.["error"]);
|
|
26120
|
-
const
|
|
26291
|
+
const code2 = stringOf(record2, "code") ?? `http_${status}`;
|
|
26121
26292
|
const message = stringOf(record2, "message") ?? `the API answered ${status}`;
|
|
26122
26293
|
appendLine(options.path, {
|
|
26123
26294
|
op: "dropped",
|
|
26124
26295
|
key: entry.key,
|
|
26125
|
-
code,
|
|
26296
|
+
code: code2,
|
|
26126
26297
|
message,
|
|
26127
26298
|
droppedAt: now().toISOString()
|
|
26128
26299
|
});
|
|
@@ -26176,23 +26347,23 @@ function actAsProblem(context) {
|
|
|
26176
26347
|
if (context.credential !== void 0) {
|
|
26177
26348
|
return `you are logged in to ${endpoint}`;
|
|
26178
26349
|
}
|
|
26179
|
-
if (!
|
|
26350
|
+
if (!isLoopbackUrl(endpoint)) return `${endpoint} is not a loopback endpoint`;
|
|
26180
26351
|
return void 0;
|
|
26181
26352
|
}
|
|
26182
26353
|
function checkActAs(context) {
|
|
26183
26354
|
const problem = actAsProblem(context);
|
|
26184
26355
|
if (problem !== void 0) throw actAsNotApplicable(problem);
|
|
26185
26356
|
}
|
|
26186
|
-
function openClient(context, io, options = {}) {
|
|
26357
|
+
async function openClient(context, io, options = {}) {
|
|
26358
|
+
const endpoint = await resolveEndpoint(context, io);
|
|
26187
26359
|
checkActAs(context);
|
|
26188
|
-
const
|
|
26189
|
-
const http = { debug: context.debug, fetch: io.fetch };
|
|
26360
|
+
const http = { debug: context.debug, fetch: io.fetch, timeoutMs: context.timeoutMs };
|
|
26190
26361
|
const org = {
|
|
26191
26362
|
...context.org === void 0 ? {} : { org: context.org },
|
|
26192
26363
|
...options.queue === false ? {} : { beforeFirst: queueSender(context, endpoint, io) }
|
|
26193
26364
|
};
|
|
26194
26365
|
if (context.token === void 0 && context.credential === void 0) {
|
|
26195
|
-
if (!
|
|
26366
|
+
if (!isLoopbackUrl(endpoint)) throw notLoggedIn(endpoint);
|
|
26196
26367
|
return new ApiClient({
|
|
26197
26368
|
endpoint,
|
|
26198
26369
|
authorizer: devAuthorizer(endpoint, context.actAs, http),
|
|
@@ -26207,26 +26378,27 @@ function openClient(context, io, options = {}) {
|
|
|
26207
26378
|
...http
|
|
26208
26379
|
});
|
|
26209
26380
|
}
|
|
26210
|
-
function openOrgClient(context, io) {
|
|
26211
|
-
checkActAs(context);
|
|
26381
|
+
async function openOrgClient(context, io, options = {}) {
|
|
26382
|
+
if (context.mode === "local") checkActAs(context);
|
|
26212
26383
|
requireOrg(context);
|
|
26213
|
-
return openClient(context, io);
|
|
26384
|
+
return openClient(context, io, options);
|
|
26214
26385
|
}
|
|
26215
|
-
function openBackend(context, io) {
|
|
26386
|
+
async function openBackend(context, io) {
|
|
26216
26387
|
if (context.mode === "local") {
|
|
26217
26388
|
checkActAs(context);
|
|
26389
|
+
refuseForeignOrg(context);
|
|
26218
26390
|
const local = new FileStore(context.storePath);
|
|
26219
26391
|
return { store: local, client: void 0, local };
|
|
26220
26392
|
}
|
|
26221
|
-
const client = openOrgClient(context, io);
|
|
26393
|
+
const client = await openOrgClient(context, io);
|
|
26222
26394
|
return { store: new RemoteStore(client), client, local: void 0 };
|
|
26223
26395
|
}
|
|
26224
|
-
function openStore(context, io) {
|
|
26225
|
-
return openBackend(context, io).store;
|
|
26396
|
+
async function openStore(context, io) {
|
|
26397
|
+
return (await openBackend(context, io)).store;
|
|
26226
26398
|
}
|
|
26227
26399
|
function needsServer(command, context) {
|
|
26228
26400
|
return new ConfigError(
|
|
26229
|
-
|
|
26401
|
+
`${command} needs a server: ${context.localFix ?? "drop --local"}`
|
|
26230
26402
|
);
|
|
26231
26403
|
}
|
|
26232
26404
|
function requireServer(command, context) {
|
|
@@ -26242,7 +26414,7 @@ async function runComplain(message, options, context, io) {
|
|
|
26242
26414
|
throw new UsageError(REMOTE_REPORTER_MESSAGE);
|
|
26243
26415
|
}
|
|
26244
26416
|
const body = await readMessage(message, io);
|
|
26245
|
-
const store = openStore(context, io);
|
|
26417
|
+
const store = await openStore(context, io);
|
|
26246
26418
|
const input2 = {
|
|
26247
26419
|
org,
|
|
26248
26420
|
project,
|
|
@@ -26266,6 +26438,7 @@ async function runComplain(message, options, context, io) {
|
|
|
26266
26438
|
}
|
|
26267
26439
|
if (options.json === true) {
|
|
26268
26440
|
writeJson(io.stdout, complaint);
|
|
26441
|
+
localNotice(context, io);
|
|
26269
26442
|
return 0;
|
|
26270
26443
|
}
|
|
26271
26444
|
const scope = context.mode === "remote" ? `${complaint.org}/${complaint.project}` : complaint.project;
|
|
@@ -26274,8 +26447,16 @@ async function runComplain(message, options, context, io) {
|
|
|
26274
26447
|
`Filed complaint ${complaint.id} for ${scope} (${complaint.severity})`
|
|
26275
26448
|
);
|
|
26276
26449
|
for (const line of matchLines(complaint)) writeLine(io.stdout, line);
|
|
26450
|
+
localNotice(context, io);
|
|
26277
26451
|
return 0;
|
|
26278
26452
|
}
|
|
26453
|
+
function localNotice(context, io) {
|
|
26454
|
+
if (context.mode !== "local") return;
|
|
26455
|
+
writeLine(
|
|
26456
|
+
io.stderr,
|
|
26457
|
+
`quibble: local mode: nothing was sent; the complaint is in ${context.storePath}`
|
|
26458
|
+
);
|
|
26459
|
+
}
|
|
26279
26460
|
function matchLines(complaint) {
|
|
26280
26461
|
const lines = [];
|
|
26281
26462
|
if (complaint.openMatches === 1) {
|
|
@@ -26319,7 +26500,7 @@ function describeCredential(context) {
|
|
|
26319
26500
|
if (context.credential !== void 0) {
|
|
26320
26501
|
return `logged in as ${context.credential.email}`;
|
|
26321
26502
|
}
|
|
26322
|
-
if (context.endpoint !== void 0 &&
|
|
26503
|
+
if (context.endpoint !== void 0 && isLoopbackUrl(context.endpoint)) {
|
|
26323
26504
|
return "loopback dev token";
|
|
26324
26505
|
}
|
|
26325
26506
|
return "not logged in";
|
|
@@ -26331,14 +26512,18 @@ function describeAuth(context) {
|
|
|
26331
26512
|
if (problem === void 0) return `${credential}, acting as ${context.actAs}`;
|
|
26332
26513
|
return `${credential}; ${ACT_AS_ENV} does not apply: ${problem}`;
|
|
26333
26514
|
}
|
|
26334
|
-
function runConfig(options, context, io) {
|
|
26515
|
+
async function runConfig(options, context, io) {
|
|
26516
|
+
if (context.mode === "remote") await resolveEndpoint(context, io);
|
|
26517
|
+
const ignored = foreignOrgMessage(context) === void 0 ? void 0 : context.slug;
|
|
26335
26518
|
const report = {
|
|
26336
26519
|
project: context.project ?? "none",
|
|
26337
26520
|
org: context.org ?? null,
|
|
26521
|
+
orgIgnored: ignored ?? null,
|
|
26338
26522
|
projectRoot: context.projectRoot ?? null,
|
|
26339
26523
|
configPath: context.configPath ?? null,
|
|
26340
26524
|
mode: context.mode,
|
|
26341
|
-
endpoint: context.endpoint ??
|
|
26525
|
+
endpoint: context.endpoint ?? "local",
|
|
26526
|
+
endpointSource: context.endpointSource,
|
|
26342
26527
|
storePath: context.storePath,
|
|
26343
26528
|
agent: context.agent,
|
|
26344
26529
|
reporter: context.reporter,
|
|
@@ -26351,16 +26536,84 @@ function runConfig(options, context, io) {
|
|
|
26351
26536
|
writeJson(io.stdout, report);
|
|
26352
26537
|
return 0;
|
|
26353
26538
|
}
|
|
26354
|
-
const
|
|
26355
|
-
|
|
26356
|
-
|
|
26539
|
+
const { orgIgnored, ...lines } = report;
|
|
26540
|
+
const text = { ...lines };
|
|
26541
|
+
if (orgIgnored !== null) {
|
|
26542
|
+
text["org"] = `${report.org ?? ""}; ${orgIgnored} does not apply: ${context.localFix ?? ""}`;
|
|
26543
|
+
}
|
|
26544
|
+
const width = Math.max(...Object.keys(text).map((key) => key.length));
|
|
26545
|
+
for (const [key, value] of Object.entries(text)) {
|
|
26546
|
+
writeLine(io.stdout, `${key.padEnd(width)} ${String(value ?? "none")}`);
|
|
26357
26547
|
}
|
|
26358
26548
|
return 0;
|
|
26359
26549
|
}
|
|
26360
26550
|
|
|
26361
26551
|
// src/commands/init.ts
|
|
26362
|
-
import { existsSync as existsSync2, writeFileSync as
|
|
26363
|
-
import { basename as basename2, join as
|
|
26552
|
+
import { existsSync as existsSync2, writeFileSync as writeFileSync5 } from "node:fs";
|
|
26553
|
+
import { basename as basename2, join as join7, resolve as resolve3 } from "node:path";
|
|
26554
|
+
|
|
26555
|
+
// src/hook.ts
|
|
26556
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
26557
|
+
import { dirname as dirname5, join as join5 } from "node:path";
|
|
26558
|
+
var SETTINGS_PATH = ".claude/settings.json";
|
|
26559
|
+
var HOOK_COMMAND = "command -v quibble >/dev/null 2>&1 && quibble sync --quiet || true";
|
|
26560
|
+
var HOOK_MARK = "quibble sync --quiet";
|
|
26561
|
+
function hookEntry() {
|
|
26562
|
+
return {
|
|
26563
|
+
matcher: "startup",
|
|
26564
|
+
hooks: [{ type: "command", command: HOOK_COMMAND }]
|
|
26565
|
+
};
|
|
26566
|
+
}
|
|
26567
|
+
function isObject2(value) {
|
|
26568
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26569
|
+
}
|
|
26570
|
+
function runsSync(entry) {
|
|
26571
|
+
if (!isObject2(entry) || !Array.isArray(entry["hooks"])) return false;
|
|
26572
|
+
return entry["hooks"].some(
|
|
26573
|
+
(hook) => isObject2(hook) && typeof hook["command"] === "string" && hook["command"].includes(HOOK_MARK)
|
|
26574
|
+
);
|
|
26575
|
+
}
|
|
26576
|
+
function planHook(root) {
|
|
26577
|
+
const path = join5(root, SETTINGS_PATH);
|
|
26578
|
+
let text;
|
|
26579
|
+
try {
|
|
26580
|
+
text = readFileSync4(path, "utf8");
|
|
26581
|
+
} catch (error62) {
|
|
26582
|
+
if (!isNotFound(error62)) throw error62;
|
|
26583
|
+
const created = { hooks: { SessionStart: [hookEntry()] } };
|
|
26584
|
+
return { path, text: `${JSON.stringify(created, null, 2)}
|
|
26585
|
+
` };
|
|
26586
|
+
}
|
|
26587
|
+
const invalid = new UsageError(
|
|
26588
|
+
`${path} is not valid JSON: fix it, then run the command again`
|
|
26589
|
+
);
|
|
26590
|
+
let settings;
|
|
26591
|
+
try {
|
|
26592
|
+
settings = JSON.parse(text);
|
|
26593
|
+
} catch {
|
|
26594
|
+
throw invalid;
|
|
26595
|
+
}
|
|
26596
|
+
if (!isObject2(settings)) throw invalid;
|
|
26597
|
+
const hooks = settings["hooks"] === void 0 ? {} : settings["hooks"];
|
|
26598
|
+
if (!isObject2(hooks)) throw new UsageError(`${path}: hooks must be an object`);
|
|
26599
|
+
const start = hooks["SessionStart"] === void 0 ? [] : hooks["SessionStart"];
|
|
26600
|
+
if (!Array.isArray(start)) {
|
|
26601
|
+
throw new UsageError(`${path}: hooks.SessionStart must be an array`);
|
|
26602
|
+
}
|
|
26603
|
+
if (start.some(runsSync)) return { path, text: void 0 };
|
|
26604
|
+
const merged = {
|
|
26605
|
+
...settings,
|
|
26606
|
+
hooks: { ...hooks, SessionStart: [...start, hookEntry()] }
|
|
26607
|
+
};
|
|
26608
|
+
return { path, text: `${JSON.stringify(merged, null, 2)}
|
|
26609
|
+
` };
|
|
26610
|
+
}
|
|
26611
|
+
function applyHook(plan) {
|
|
26612
|
+
if (plan.text === void 0) return `${plan.path} already runs quibble sync`;
|
|
26613
|
+
mkdirSync4(dirname5(plan.path), { recursive: true });
|
|
26614
|
+
writeFileSync3(plan.path, plan.text, "utf8");
|
|
26615
|
+
return `Added the quibble sync hook to ${plan.path}`;
|
|
26616
|
+
}
|
|
26364
26617
|
|
|
26365
26618
|
// src/snippet.ts
|
|
26366
26619
|
import { basename } from "node:path";
|
|
@@ -26423,57 +26676,131 @@ function agentForFile(path) {
|
|
|
26423
26676
|
function hasSnippet(text) {
|
|
26424
26677
|
return text.split(/\r?\n/).some((line) => line.trimEnd() === SNIPPET_HEADING);
|
|
26425
26678
|
}
|
|
26679
|
+
function splitLines(text) {
|
|
26680
|
+
const lines = [];
|
|
26681
|
+
const pattern = /([^\r\n]*)(\r\n|\n|\r)?/g;
|
|
26682
|
+
let match;
|
|
26683
|
+
while ((match = pattern.exec(text)) !== null) {
|
|
26684
|
+
const [whole, content = "", eol = ""] = match;
|
|
26685
|
+
if (whole === "") break;
|
|
26686
|
+
lines.push({ text: content, eol });
|
|
26687
|
+
}
|
|
26688
|
+
return lines;
|
|
26689
|
+
}
|
|
26690
|
+
function fenceOf(text) {
|
|
26691
|
+
if (text.startsWith("```")) return "```";
|
|
26692
|
+
if (text.startsWith("~~~")) return "~~~";
|
|
26693
|
+
return void 0;
|
|
26694
|
+
}
|
|
26695
|
+
function endsSection(text) {
|
|
26696
|
+
return text.startsWith("# ") || text.startsWith("## ");
|
|
26697
|
+
}
|
|
26698
|
+
function agentOfText(lines) {
|
|
26699
|
+
const line = lines.find(
|
|
26700
|
+
(text) => text.includes("quibble complain") && text.includes("--agent")
|
|
26701
|
+
);
|
|
26702
|
+
if (line === void 0) return void 0;
|
|
26703
|
+
return /--agent\s+(\S+)/.exec(line)?.[1];
|
|
26704
|
+
}
|
|
26705
|
+
function findSection(text) {
|
|
26706
|
+
const lines = splitLines(text);
|
|
26707
|
+
const start = lines.findIndex((line) => line.text.trimEnd() === SNIPPET_HEADING);
|
|
26708
|
+
if (start < 0) return void 0;
|
|
26709
|
+
let next = lines.length;
|
|
26710
|
+
let fence;
|
|
26711
|
+
for (let index = start + 1; index < lines.length; index += 1) {
|
|
26712
|
+
const content = lines[index]?.text ?? "";
|
|
26713
|
+
if (fence !== void 0) {
|
|
26714
|
+
if (content.startsWith(fence)) fence = void 0;
|
|
26715
|
+
continue;
|
|
26716
|
+
}
|
|
26717
|
+
fence = fenceOf(content);
|
|
26718
|
+
if (fence === void 0 && endsSection(content)) {
|
|
26719
|
+
next = index;
|
|
26720
|
+
break;
|
|
26721
|
+
}
|
|
26722
|
+
}
|
|
26723
|
+
let end = next;
|
|
26724
|
+
while (end > start + 1 && (lines[end - 1]?.text.trim() ?? "") === "") end -= 1;
|
|
26725
|
+
const eol = lines[start]?.eol || "\n";
|
|
26726
|
+
return { lines, start, end, eol: eol === "\r\n" ? "\r\n" : "\n" };
|
|
26727
|
+
}
|
|
26728
|
+
function sectionText(section) {
|
|
26729
|
+
return section.lines.slice(section.start, section.end).map((line) => line.text);
|
|
26730
|
+
}
|
|
26731
|
+
function replaceSection(section, snippet) {
|
|
26732
|
+
const join9 = (lines) => lines.map((line) => `${line.text}${line.eol}`).join("");
|
|
26733
|
+
const body = snippet.split("\n").map((line) => `${line}${section.eol}`).join("");
|
|
26734
|
+
return `${join9(section.lines.slice(0, section.start))}${body}${join9(section.lines.slice(section.end))}`;
|
|
26735
|
+
}
|
|
26736
|
+
function withLineBreak(text, old) {
|
|
26737
|
+
return old.includes("\r\n") ? text.replace(/\r?\n/g, "\r\n") : text;
|
|
26738
|
+
}
|
|
26739
|
+
function orList(items) {
|
|
26740
|
+
if (items.length <= 1) return items.join("");
|
|
26741
|
+
return `${items.slice(0, -1).join(", ")} or ${items.at(-1) ?? ""}`;
|
|
26742
|
+
}
|
|
26743
|
+
function refreshedLine(project, tags) {
|
|
26744
|
+
const lead = `Quibble refreshed its instructions for ${project}:`;
|
|
26745
|
+
if (tags === void 0 || tags.length === 0) return `${lead} any tag is allowed.`;
|
|
26746
|
+
return `${lead} tag each complaint with ${orList(tags)}, or ${OTHER_TAG_PREFIX}<name> when none fits.`;
|
|
26747
|
+
}
|
|
26426
26748
|
|
|
26427
26749
|
// src/commands/snippet.ts
|
|
26428
|
-
import { existsSync, mkdirSync as
|
|
26429
|
-
import { dirname as
|
|
26750
|
+
import { existsSync, mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
26751
|
+
import { dirname as dirname6, join as join6, resolve as resolve2 } from "node:path";
|
|
26430
26752
|
function existsError(path) {
|
|
26431
26753
|
return new UsageError(`${path} already exists: pass --force to overwrite it`);
|
|
26432
26754
|
}
|
|
26433
26755
|
function readOptional2(path) {
|
|
26434
26756
|
try {
|
|
26435
|
-
return
|
|
26757
|
+
return readFileSync5(path, "utf8");
|
|
26436
26758
|
} catch (error62) {
|
|
26437
26759
|
if (isNotFound(error62)) return "";
|
|
26438
26760
|
throw error62;
|
|
26439
26761
|
}
|
|
26440
26762
|
}
|
|
26441
26763
|
function writeSkill(path, snippet) {
|
|
26442
|
-
|
|
26443
|
-
|
|
26764
|
+
mkdirSync5(dirname6(path), { recursive: true });
|
|
26765
|
+
writeFileSync4(path, skillFile(snippet), "utf8");
|
|
26766
|
+
}
|
|
26767
|
+
async function snippetSource(project, context, io, options = {}) {
|
|
26768
|
+
let tags;
|
|
26769
|
+
if (context.mode === "remote") {
|
|
26770
|
+
tags = (await (await openOrgClient(context, io, options)).project(project)).tags;
|
|
26771
|
+
} else {
|
|
26772
|
+
checkActAs(context);
|
|
26773
|
+
tags = context.allowedTags;
|
|
26774
|
+
}
|
|
26775
|
+
const scope = {
|
|
26776
|
+
...context.mode === "remote" ? { endpoint: context.endpoint } : {},
|
|
26777
|
+
...context.slug === void 0 ? {} : { org: context.slug }
|
|
26778
|
+
};
|
|
26779
|
+
return { tags, scope };
|
|
26444
26780
|
}
|
|
26445
26781
|
async function runSnippet(options, context, io) {
|
|
26446
26782
|
const project = requireProject(context);
|
|
26447
26783
|
const override = options.agent === void 0 ? void 0 : parseAgentName(options.agent);
|
|
26448
26784
|
const appendPath = options.append === void 0 ? void 0 : resolve2(io.cwd, options.append);
|
|
26449
|
-
const skillPath = options.skill === true ?
|
|
26785
|
+
const skillPath = options.skill === true ? join6(context.projectRoot ?? io.cwd, SKILL_PATH) : void 0;
|
|
26450
26786
|
const existing = appendPath === void 0 ? "" : readOptional2(appendPath);
|
|
26451
26787
|
if (options.append !== void 0 && hasSnippet(existing)) {
|
|
26452
26788
|
throw new UsageError(
|
|
26453
|
-
`${options.append} already has a "${SNIPPET_HEADING}" section:
|
|
26789
|
+
`${options.append} already has a "${SNIPPET_HEADING}" section: run quibble sync to refresh it`
|
|
26454
26790
|
);
|
|
26455
26791
|
}
|
|
26456
26792
|
if (skillPath !== void 0 && existsSync(skillPath) && options.force !== true) {
|
|
26457
26793
|
throw existsError(skillPath);
|
|
26458
26794
|
}
|
|
26459
|
-
|
|
26460
|
-
|
|
26461
|
-
tags = (await openOrgClient(context, io).project(project)).tags;
|
|
26462
|
-
} else {
|
|
26463
|
-
checkActAs(context);
|
|
26464
|
-
tags = context.allowedTags;
|
|
26465
|
-
}
|
|
26466
|
-
const scope = {
|
|
26467
|
-
...context.mode === "remote" ? { endpoint: context.endpoint } : {},
|
|
26468
|
-
...context.slug === void 0 ? {} : { org: context.slug }
|
|
26469
|
-
};
|
|
26795
|
+
const hook = options.hook === true ? planHook(context.projectRoot ?? io.cwd) : void 0;
|
|
26796
|
+
const { tags, scope } = await snippetSource(project, context, io);
|
|
26470
26797
|
let shown = { agent: override ?? DEFAULT_SNIPPET_AGENT, snippet: "" };
|
|
26471
26798
|
shown.snippet = agentSnippet(tags, { ...scope, agent: shown.agent });
|
|
26472
26799
|
if (appendPath !== void 0) {
|
|
26473
26800
|
const agent = override ?? agentForFile(appendPath);
|
|
26474
26801
|
const snippet = agentSnippet(tags, { ...scope, agent });
|
|
26475
26802
|
const kept = existing.replace(/\s+$/, "");
|
|
26476
|
-
|
|
26803
|
+
writeFileSync4(appendPath, kept === "" ? `${snippet}
|
|
26477
26804
|
` : `${kept}
|
|
26478
26805
|
|
|
26479
26806
|
${snippet}
|
|
@@ -26486,23 +26813,25 @@ ${snippet}
|
|
|
26486
26813
|
writeSkill(skillPath, snippet);
|
|
26487
26814
|
if (appendPath === void 0) shown = { agent, snippet };
|
|
26488
26815
|
}
|
|
26816
|
+
const hookLine = hook === void 0 ? void 0 : applyHook(hook);
|
|
26489
26817
|
if (options.json === true) {
|
|
26490
26818
|
writeJson(io.stdout, {
|
|
26491
26819
|
snippet: shown.snippet,
|
|
26492
26820
|
agent: shown.agent,
|
|
26493
26821
|
appended: appendPath ?? null,
|
|
26494
|
-
skill: skillPath ?? null
|
|
26822
|
+
skill: skillPath ?? null,
|
|
26823
|
+
hook: hook?.path ?? null
|
|
26495
26824
|
});
|
|
26496
26825
|
return 0;
|
|
26497
26826
|
}
|
|
26498
26827
|
if (appendPath === void 0 && skillPath === void 0) {
|
|
26499
26828
|
writeLine(io.stdout, shown.snippet);
|
|
26500
|
-
return 0;
|
|
26501
26829
|
}
|
|
26502
26830
|
if (appendPath !== void 0) {
|
|
26503
26831
|
writeLine(io.stdout, `Appended the Quibble snippet to ${appendPath}`);
|
|
26504
26832
|
}
|
|
26505
26833
|
if (skillPath !== void 0) writeLine(io.stdout, `Wrote ${skillPath}`);
|
|
26834
|
+
if (hookLine !== void 0) writeLine(io.stdout, hookLine);
|
|
26506
26835
|
return 0;
|
|
26507
26836
|
}
|
|
26508
26837
|
|
|
@@ -26513,34 +26842,47 @@ function agentLead(agent) {
|
|
|
26513
26842
|
}
|
|
26514
26843
|
async function runInit(options, io) {
|
|
26515
26844
|
const directory = resolve3(io.cwd);
|
|
26516
|
-
const path =
|
|
26845
|
+
const path = join7(directory, PROJECT_CONFIG_FILENAME);
|
|
26517
26846
|
if (existsSync2(path) && options.force !== true) {
|
|
26518
26847
|
throw new ConfigError(`${path} already exists: pass --force to overwrite it`, path);
|
|
26519
26848
|
}
|
|
26520
|
-
const skillPath = options.skill === true ?
|
|
26849
|
+
const skillPath = options.skill === true ? join7(directory, SKILL_PATH) : void 0;
|
|
26521
26850
|
if (skillPath !== void 0 && existsSync2(skillPath) && options.force !== true) {
|
|
26522
26851
|
throw existsError(skillPath);
|
|
26523
26852
|
}
|
|
26524
26853
|
const project = resolveProjectName(options.project, directory);
|
|
26525
26854
|
const org = options.org === void 0 || options.org.trim() === "" ? void 0 : parseOrgSlug(options.org);
|
|
26526
|
-
const endpoint = options.endpoint?.trim();
|
|
26527
26855
|
const tags = options.tag === void 0 ? void 0 : parseAllowedTags(options.tag);
|
|
26528
26856
|
const agent = options.agent === void 0 ? DEFAULT_SNIPPET_AGENT : parseAgentName(options.agent);
|
|
26857
|
+
const hook = options.hook === true ? planHook(directory) : void 0;
|
|
26858
|
+
const endpoint = await endpointToWrite(options.endpoint, io);
|
|
26529
26859
|
const config2 = {
|
|
26530
26860
|
project,
|
|
26531
26861
|
...org === void 0 ? {} : { org },
|
|
26532
|
-
...endpoint === void 0
|
|
26862
|
+
...endpoint === void 0 ? {} : { endpoint },
|
|
26533
26863
|
...tags === void 0 ? {} : { tags }
|
|
26534
26864
|
};
|
|
26535
|
-
|
|
26865
|
+
writeFileSync5(path, `${JSON.stringify(config2, null, 2)}
|
|
26536
26866
|
`, "utf8");
|
|
26867
|
+
const context = {
|
|
26868
|
+
...resolveContext({
|
|
26869
|
+
cwd: io.cwd,
|
|
26870
|
+
env: io.env,
|
|
26871
|
+
endpoint: options.endpoint,
|
|
26872
|
+
org: options.org,
|
|
26873
|
+
local: options.local
|
|
26874
|
+
}),
|
|
26875
|
+
// `init` acts as nobody, so it ignores `QUIBBLE_ACT_AS`.
|
|
26876
|
+
actAs: void 0
|
|
26877
|
+
};
|
|
26537
26878
|
const snippet = agentSnippet(tags, {
|
|
26538
26879
|
agent,
|
|
26539
|
-
...
|
|
26880
|
+
...context.mode === "remote" ? { endpoint: context.endpoint } : {},
|
|
26540
26881
|
...org === void 0 ? {} : { org }
|
|
26541
26882
|
});
|
|
26542
26883
|
if (skillPath !== void 0) writeSkill(skillPath, snippet);
|
|
26543
|
-
const
|
|
26884
|
+
const hookLine = hook === void 0 ? void 0 : applyHook(hook);
|
|
26885
|
+
const check2 = await checkServer(context, project, io);
|
|
26544
26886
|
if (options.json === true) {
|
|
26545
26887
|
writeJson(io.stdout, {
|
|
26546
26888
|
path,
|
|
@@ -26548,12 +26890,14 @@ async function runInit(options, io) {
|
|
|
26548
26890
|
org: org ?? null,
|
|
26549
26891
|
tags: tags ?? null,
|
|
26550
26892
|
skill: skillPath ?? null,
|
|
26893
|
+
hook: hook?.path ?? null,
|
|
26551
26894
|
server: check2.server
|
|
26552
26895
|
});
|
|
26553
26896
|
return 0;
|
|
26554
26897
|
}
|
|
26555
26898
|
writeLine(io.stdout, `Wrote ${path}`);
|
|
26556
26899
|
if (skillPath !== void 0) writeLine(io.stdout, `Wrote ${skillPath}`);
|
|
26900
|
+
if (hookLine !== void 0) writeLine(io.stdout, hookLine);
|
|
26557
26901
|
writeLine(io.stdout, `Project: ${project}`);
|
|
26558
26902
|
if (org !== void 0) writeLine(io.stdout, `Organization: ${org}`);
|
|
26559
26903
|
if (tags !== void 0) writeLine(io.stdout, `Tags: ${tags.join(", ")}`);
|
|
@@ -26571,26 +26915,28 @@ async function runInit(options, io) {
|
|
|
26571
26915
|
}
|
|
26572
26916
|
return 0;
|
|
26573
26917
|
}
|
|
26574
|
-
async function
|
|
26575
|
-
|
|
26576
|
-
|
|
26577
|
-
|
|
26578
|
-
|
|
26579
|
-
|
|
26580
|
-
|
|
26581
|
-
|
|
26582
|
-
|
|
26583
|
-
|
|
26584
|
-
|
|
26585
|
-
const
|
|
26586
|
-
if (context.mode !== "remote" ||
|
|
26918
|
+
async function endpointToWrite(given, io) {
|
|
26919
|
+
if (given === void 0 || given.trim() === "") return void 0;
|
|
26920
|
+
const value = parseEndpoint(given, "--endpoint");
|
|
26921
|
+
if (value.kind === "local") return LOCAL_ENDPOINT;
|
|
26922
|
+
const api = await discoverApiUrl(value.url, {
|
|
26923
|
+
fetch: io.fetch,
|
|
26924
|
+
debug: debugEnabled(io.env)
|
|
26925
|
+
});
|
|
26926
|
+
return isDefaultEndpoint(api) ? void 0 : api;
|
|
26927
|
+
}
|
|
26928
|
+
async function checkServer(context, project, io) {
|
|
26929
|
+
const org = context.org;
|
|
26930
|
+
if (context.mode !== "remote" || org === void 0) return { server: "unchecked" };
|
|
26931
|
+
const endpoint = await resolveEndpoint(context, io);
|
|
26932
|
+
if (context.token === void 0 && context.credential === void 0 && !isLoopbackUrl(endpoint)) {
|
|
26587
26933
|
return { server: "unchecked" };
|
|
26588
26934
|
}
|
|
26589
26935
|
const warn = (text) => {
|
|
26590
26936
|
writeLine(io.stderr, `quibble: warning: ${text}`);
|
|
26591
26937
|
};
|
|
26592
26938
|
try {
|
|
26593
|
-
await openClient(context, io, { queue: false }).project(project);
|
|
26939
|
+
await (await openClient(context, io, { queue: false })).project(project);
|
|
26594
26940
|
return { server: "found", line: `Project ${org}/${project} exists on ${endpoint}.` };
|
|
26595
26941
|
} catch (error62) {
|
|
26596
26942
|
if (error62 instanceof RemoteError && error62.status === 404) {
|
|
@@ -26760,7 +27106,7 @@ async function runList(options, context, io) {
|
|
|
26760
27106
|
if (options.ids === true && options.json === true) {
|
|
26761
27107
|
throw new UsageError("--ids and --json cannot be used together");
|
|
26762
27108
|
}
|
|
26763
|
-
const { store, client } = openBackend(context, io);
|
|
27109
|
+
const { store, client } = await openBackend(context, io);
|
|
26764
27110
|
const reporter = options.mine === true ? await resolveMine(context, client, io.env) : void 0;
|
|
26765
27111
|
const filter = parseListFilter({
|
|
26766
27112
|
...resolveScope(options, context),
|
|
@@ -26835,13 +27181,13 @@ function formatTable(rows2) {
|
|
|
26835
27181
|
}
|
|
26836
27182
|
|
|
26837
27183
|
// src/commands/orgs.ts
|
|
26838
|
-
var ORGS_NEEDS_SERVER = "orgs needs a server: pass --endpoint <url>, set QUIBBLE_ENDPOINT, or add one to .quibblerc";
|
|
26839
27184
|
var ORGS_PAGE = "/orgs";
|
|
26840
27185
|
var COLUMNS2 = ["SLUG", "NAME", "ROLE", "OPEN"];
|
|
26841
27186
|
async function runOrgs(options, context, io) {
|
|
26842
|
-
|
|
27187
|
+
requireServer("orgs", context);
|
|
27188
|
+
const client = await openClient(context, io);
|
|
26843
27189
|
const endpoint = requireEndpoint(context);
|
|
26844
|
-
const answer2 = await
|
|
27190
|
+
const answer2 = await client.orgs();
|
|
26845
27191
|
if (options.json === true) {
|
|
26846
27192
|
writeJson(io.stdout, answer2.body);
|
|
26847
27193
|
return 0;
|
|
@@ -26849,9 +27195,10 @@ async function runOrgs(options, context, io) {
|
|
|
26849
27195
|
if (answer2.orgs.length === 0) {
|
|
26850
27196
|
const config2 = await fetchAuthConfig(endpoint, {
|
|
26851
27197
|
debug: context.debug,
|
|
26852
|
-
fetch: io.fetch
|
|
27198
|
+
fetch: io.fetch,
|
|
27199
|
+
timeoutMs: context.timeoutMs
|
|
26853
27200
|
});
|
|
26854
|
-
const webUrl = config2.mode === "cognito" ?
|
|
27201
|
+
const webUrl = config2.mode === "cognito" ? trimSlash2(config2.webUrl) : endpoint;
|
|
26855
27202
|
writeLine(io.stdout, `No organizations yet: create one at ${webUrl}${ORGS_PAGE}.`);
|
|
26856
27203
|
return 0;
|
|
26857
27204
|
}
|
|
@@ -26911,19 +27258,19 @@ var NodeTerminal = class {
|
|
|
26911
27258
|
#interface() {
|
|
26912
27259
|
if (this.#rl !== void 0) return this.#rl;
|
|
26913
27260
|
const output2 = this.#output;
|
|
26914
|
-
const
|
|
27261
|
+
const sink2 = new Writable({
|
|
26915
27262
|
write: (chunk, _encoding, done) => {
|
|
26916
27263
|
if (!this.#muted) output2.write(chunk);
|
|
26917
27264
|
done();
|
|
26918
27265
|
}
|
|
26919
27266
|
});
|
|
26920
|
-
Object.defineProperties(
|
|
27267
|
+
Object.defineProperties(sink2, {
|
|
26921
27268
|
columns: { get: () => output2.columns },
|
|
26922
27269
|
rows: { get: () => output2.rows }
|
|
26923
27270
|
});
|
|
26924
27271
|
const rl = createInterface({
|
|
26925
27272
|
input: this.#input,
|
|
26926
|
-
output:
|
|
27273
|
+
output: sink2,
|
|
26927
27274
|
terminal: true,
|
|
26928
27275
|
// No answer enters the history, so an arrow key recalls no password.
|
|
26929
27276
|
historySize: 0
|
|
@@ -27023,19 +27370,19 @@ async function signIn(terminal, identity, config2) {
|
|
|
27023
27370
|
password
|
|
27024
27371
|
});
|
|
27025
27372
|
if (result.kind === "challenge" && result.challenge.challengeName === MFA_CHALLENGE) {
|
|
27026
|
-
const
|
|
27373
|
+
const code2 = await answer(terminal, "Code:");
|
|
27027
27374
|
result = await respondToAuthChallenge(identity, {
|
|
27028
27375
|
clientId,
|
|
27029
27376
|
challenge: result.challenge,
|
|
27030
27377
|
email: email3,
|
|
27031
|
-
code
|
|
27378
|
+
code: code2
|
|
27032
27379
|
});
|
|
27033
27380
|
}
|
|
27034
27381
|
if (result.kind === "challenge") {
|
|
27035
27382
|
const name = result.challenge.challengeName;
|
|
27036
27383
|
if (name === NEW_PASSWORD_CHALLENGE) {
|
|
27037
27384
|
throw new ConfigError(
|
|
27038
|
-
`finish your first sign-in at ${
|
|
27385
|
+
`finish your first sign-in at ${trimSlash2(config2.webUrl)}${LOGIN_PAGE}`
|
|
27039
27386
|
);
|
|
27040
27387
|
}
|
|
27041
27388
|
throw new RemoteError(
|
|
@@ -27045,7 +27392,7 @@ async function signIn(terminal, identity, config2) {
|
|
|
27045
27392
|
return result.tokens;
|
|
27046
27393
|
} catch (error62) {
|
|
27047
27394
|
if (error62 instanceof CognitoError) {
|
|
27048
|
-
const page = `${
|
|
27395
|
+
const page = `${trimSlash2(config2.webUrl)}${LOGIN_PAGE}`;
|
|
27049
27396
|
if (error62.type === "PasswordResetRequiredException") {
|
|
27050
27397
|
throw new ConfigError(`reset your password at ${page}`);
|
|
27051
27398
|
}
|
|
@@ -27057,9 +27404,10 @@ async function signIn(terminal, identity, config2) {
|
|
|
27057
27404
|
}
|
|
27058
27405
|
}
|
|
27059
27406
|
async function runLogin(options, context, io) {
|
|
27060
|
-
|
|
27407
|
+
requireServer("login", context);
|
|
27061
27408
|
const terminal = terminalOf(io);
|
|
27062
27409
|
if (!terminal.isTTY) throw new UsageError(NEEDS_TERMINAL_MESSAGE);
|
|
27410
|
+
const endpoint = await resolveEndpoint(context, io);
|
|
27063
27411
|
const http = { debug: context.debug, fetch: io.fetch };
|
|
27064
27412
|
const config2 = await fetchAuthConfig(endpoint, http);
|
|
27065
27413
|
if (config2.mode === "local") {
|
|
@@ -27097,25 +27445,36 @@ async function runLogin(options, context, io) {
|
|
|
27097
27445
|
sub: me.sub,
|
|
27098
27446
|
email: me.email
|
|
27099
27447
|
});
|
|
27448
|
+
const saved = saveEndpoint(context, endpoint, io.env);
|
|
27100
27449
|
if (options.json === true) {
|
|
27101
|
-
writeJson(io.stdout, { ...me.body, endpoint });
|
|
27450
|
+
writeJson(io.stdout, { ...me.body, endpoint, saved: saved ?? null });
|
|
27102
27451
|
return 0;
|
|
27103
27452
|
}
|
|
27104
27453
|
writeLine(io.stdout, `Logged in to ${endpoint} as ${me.email}.`);
|
|
27454
|
+
if (saved !== void 0) {
|
|
27455
|
+
writeLine(io.stdout, `Saved ${endpoint} as your endpoint in ${saved}.`);
|
|
27456
|
+
}
|
|
27105
27457
|
return 0;
|
|
27106
27458
|
}
|
|
27459
|
+
function saveEndpoint(context, endpoint, env) {
|
|
27460
|
+
if (context.endpointSource !== "--endpoint" || isDefaultEndpoint(endpoint)) {
|
|
27461
|
+
return void 0;
|
|
27462
|
+
}
|
|
27463
|
+
return rememberEndpoint(env, endpoint);
|
|
27464
|
+
}
|
|
27107
27465
|
async function readProfile(client, webUrl) {
|
|
27108
27466
|
try {
|
|
27109
27467
|
return await client.me();
|
|
27110
27468
|
} catch (error62) {
|
|
27111
27469
|
if (error62 instanceof RemoteError && NO_PROFILE_STATUSES.has(error62.status ?? 0)) {
|
|
27112
|
-
throw new ConfigError(`finish your account at ${
|
|
27470
|
+
throw new ConfigError(`finish your account at ${trimSlash2(webUrl)}${LOGIN_PAGE}`);
|
|
27113
27471
|
}
|
|
27114
27472
|
throw error62;
|
|
27115
27473
|
}
|
|
27116
27474
|
}
|
|
27117
27475
|
async function runLogout(context, io) {
|
|
27118
|
-
|
|
27476
|
+
requireServer("logout", context);
|
|
27477
|
+
const endpoint = await resolveEndpoint(context, io);
|
|
27119
27478
|
const credential = findCredential(io.env, endpoint);
|
|
27120
27479
|
if (credential === void 0) {
|
|
27121
27480
|
writeLine(io.stdout, `Not logged in to ${endpoint}.`);
|
|
@@ -27151,8 +27510,8 @@ async function runWhoami(options, context, io) {
|
|
|
27151
27510
|
writeLine(io.stdout, LOCAL_IDENTITY_MESSAGE);
|
|
27152
27511
|
return 0;
|
|
27153
27512
|
}
|
|
27513
|
+
const client = await openClient(context, io);
|
|
27154
27514
|
const endpoint = requireEndpoint(context);
|
|
27155
|
-
const client = openClient(context, io);
|
|
27156
27515
|
const me = await client.me();
|
|
27157
27516
|
const org = me.kind === "token" || client.org === void 0 ? void 0 : await client.organization();
|
|
27158
27517
|
if (options.json === true) {
|
|
@@ -27169,7 +27528,7 @@ async function runWhoami(options, context, io) {
|
|
|
27169
27528
|
debug: context.debug,
|
|
27170
27529
|
fetch: io.fetch
|
|
27171
27530
|
});
|
|
27172
|
-
const webUrl = config2.mode === "cognito" ?
|
|
27531
|
+
const webUrl = config2.mode === "cognito" ? trimSlash2(config2.webUrl) : endpoint;
|
|
27173
27532
|
lines.push("", `Create one at ${webUrl}${ORGS_PAGE2}.`);
|
|
27174
27533
|
}
|
|
27175
27534
|
for (const line of lines) writeLine(io.stdout, line);
|
|
@@ -27209,18 +27568,40 @@ function whoamiLines(endpoint, me, org) {
|
|
|
27209
27568
|
return lines;
|
|
27210
27569
|
}
|
|
27211
27570
|
|
|
27571
|
+
// src/commands/project.ts
|
|
27572
|
+
var DELETE_NEEDS_YES = "project delete needs --yes when stdin is not a terminal";
|
|
27573
|
+
var DELETE_MISMATCH = "the name did not match: nothing was deleted";
|
|
27574
|
+
async function confirm(org, project, io) {
|
|
27575
|
+
const terminal = io.terminal ?? nodeTerminal(io.stdin, io.stderr);
|
|
27576
|
+
if (!terminal.isTTY) throw new UsageError(DELETE_NEEDS_YES);
|
|
27577
|
+
let answer2;
|
|
27578
|
+
try {
|
|
27579
|
+
answer2 = await terminal.prompt(`Type the project name to delete ${org}/${project}:`);
|
|
27580
|
+
} finally {
|
|
27581
|
+
terminal.close();
|
|
27582
|
+
}
|
|
27583
|
+
if (foldProjectName(answer2.trim()) !== project) throw new UsageError(DELETE_MISMATCH);
|
|
27584
|
+
}
|
|
27585
|
+
async function runProjectDelete(name, options, context, io) {
|
|
27586
|
+
requireServer("project delete", context);
|
|
27587
|
+
const project = parseProjectName(name);
|
|
27588
|
+
const org = requireOrg(context);
|
|
27589
|
+
if (options.yes !== true) await confirm(org, project, io);
|
|
27590
|
+
await (await openOrgClient(context, io)).deleteProject(project);
|
|
27591
|
+
if (options.json === true) {
|
|
27592
|
+
writeJson(io.stdout, { org, project, deleted: true });
|
|
27593
|
+
return 0;
|
|
27594
|
+
}
|
|
27595
|
+
writeLine(io.stdout, `Deleted project ${org}/${project}.`);
|
|
27596
|
+
return 0;
|
|
27597
|
+
}
|
|
27598
|
+
|
|
27212
27599
|
// src/commands/promote.ts
|
|
27213
|
-
var PROMOTE_NEEDS_SERVER = "promote needs a server: pass --endpoint <url>, set QUIBBLE_ENDPOINT, or add one to .quibblerc";
|
|
27214
|
-
var PROMOTE_NEEDS_SERVER_LOCAL = "promote needs a server: drop --local";
|
|
27215
27600
|
async function runPromote(tag, options, context, io) {
|
|
27216
|
-
|
|
27217
|
-
throw new ConfigError(
|
|
27218
|
-
context.endpoint === void 0 ? PROMOTE_NEEDS_SERVER : PROMOTE_NEEDS_SERVER_LOCAL
|
|
27219
|
-
);
|
|
27220
|
-
}
|
|
27601
|
+
requireServer("promote", context);
|
|
27221
27602
|
const project = requireProject(context);
|
|
27222
27603
|
const promotion = parseTagPromotion({ tag });
|
|
27223
|
-
const result = await openOrgClient(context, io).promoteTag(project, promotion.tag);
|
|
27604
|
+
const result = await (await openOrgClient(context, io)).promoteTag(project, promotion.tag);
|
|
27224
27605
|
if (options.json === true) {
|
|
27225
27606
|
writeJson(io.stdout, {
|
|
27226
27607
|
project,
|
|
@@ -27245,7 +27626,7 @@ var NO_TAG_MESSAGE = "retag needs at least one --tag";
|
|
|
27245
27626
|
async function runRetag(value, options, context, io) {
|
|
27246
27627
|
const tags = options.tag ?? [];
|
|
27247
27628
|
if (tags.length === 0) throw new UsageError(NO_TAG_MESSAGE);
|
|
27248
|
-
const { store, client, local } = openBackend(context, io);
|
|
27629
|
+
const { store, client, local } = await openBackend(context, io);
|
|
27249
27630
|
const [id] = await resolveIds([value], { store, local, context });
|
|
27250
27631
|
if (id === void 0) throw new NotFoundError(`no complaint with id ${value}`);
|
|
27251
27632
|
const allowedTags = client === void 0 ? await localAllowedTags(store, id, context) : void 0;
|
|
@@ -27274,9 +27655,9 @@ async function localAllowedTags(store, id, context) {
|
|
|
27274
27655
|
// src/commands/retry.ts
|
|
27275
27656
|
async function runRetry(options, context, io) {
|
|
27276
27657
|
requireServer("retry", context);
|
|
27658
|
+
const client = await openClient(context, io, { queue: false });
|
|
27277
27659
|
const endpoint = requireEndpoint(context);
|
|
27278
27660
|
const path = queuePath(io.env);
|
|
27279
|
-
const client = openClient(context, io, { queue: false });
|
|
27280
27661
|
const result = await flushQueue({
|
|
27281
27662
|
path,
|
|
27282
27663
|
endpoint,
|
|
@@ -27348,7 +27729,7 @@ function showLines(complaint, groupCount, remote) {
|
|
|
27348
27729
|
return [...lines, "", printableLines(complaint.message)];
|
|
27349
27730
|
}
|
|
27350
27731
|
async function runShow(value, options, context, io) {
|
|
27351
|
-
const { store, local } = openBackend(context, io);
|
|
27732
|
+
const { store, local } = await openBackend(context, io);
|
|
27352
27733
|
const [id] = await resolveIds([value], { store, local, context });
|
|
27353
27734
|
const complaint = id === void 0 ? void 0 : await getComplaint(store, id);
|
|
27354
27735
|
if (id === void 0 || complaint === void 0) {
|
|
@@ -27403,7 +27784,7 @@ async function runStatusChange(status, values, options, context, io) {
|
|
|
27403
27784
|
};
|
|
27404
27785
|
parseStatusChange(draft);
|
|
27405
27786
|
const given = await readIdArguments(values, io);
|
|
27406
|
-
const backend = openBackend(context, io);
|
|
27787
|
+
const backend = await openBackend(context, io);
|
|
27407
27788
|
const extra = options.duplicateOf === void 0 ? [] : [options.duplicateOf];
|
|
27408
27789
|
const resolved = await resolveIds([...given, ...extra], {
|
|
27409
27790
|
store: backend.store,
|
|
@@ -27442,10 +27823,10 @@ async function runStatusChange(status, values, options, context, io) {
|
|
|
27442
27823
|
writeLine(io.stdout, successLine(status, result.complaint));
|
|
27443
27824
|
continue;
|
|
27444
27825
|
}
|
|
27445
|
-
const
|
|
27446
|
-
const message =
|
|
27826
|
+
const code2 = result.error?.code ?? NOT_FOUND;
|
|
27827
|
+
const message = code2 === NOT_FOUND ? noComplaint(result.id) : result.error?.message;
|
|
27447
27828
|
writeLine(io.stderr, `quibble: ${result.id}: ${message ?? "the change failed"}`);
|
|
27448
|
-
failures.push({ remote: backend.store instanceof RemoteStore &&
|
|
27829
|
+
failures.push({ remote: backend.store instanceof RemoteStore && code2 !== NOT_FOUND });
|
|
27449
27830
|
}
|
|
27450
27831
|
if (options.json === true) writeJson(io.stdout, results);
|
|
27451
27832
|
if (error62 !== void 0) throw error62;
|
|
@@ -27499,7 +27880,7 @@ async function runSummary(options, context, io) {
|
|
|
27499
27880
|
throw new UsageError("--json and --format cannot be used together");
|
|
27500
27881
|
}
|
|
27501
27882
|
const format = readFormat(options.format);
|
|
27502
|
-
const { store, client } = openBackend(context, io);
|
|
27883
|
+
const { store, client } = await openBackend(context, io);
|
|
27503
27884
|
const reporter = options.mine === true ? await resolveMine(context, client, io.env) : void 0;
|
|
27504
27885
|
const filter = parseListFilter({
|
|
27505
27886
|
...resolveScope(options, context),
|
|
@@ -27609,11 +27990,127 @@ function markdownLines(filter, summary, note) {
|
|
|
27609
27990
|
return lines;
|
|
27610
27991
|
}
|
|
27611
27992
|
|
|
27993
|
+
// src/commands/sync.ts
|
|
27994
|
+
import { existsSync as existsSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "node:fs";
|
|
27995
|
+
import { join as join8 } from "node:path";
|
|
27996
|
+
import { Writable as Writable2 } from "node:stream";
|
|
27997
|
+
var SYNC_TIMEOUT_MS = 3e3;
|
|
27998
|
+
var SECTION_FILES = ["CLAUDE.md", "AGENTS.md"];
|
|
27999
|
+
function readOptional3(path) {
|
|
28000
|
+
try {
|
|
28001
|
+
return readFileSync6(path, "utf8");
|
|
28002
|
+
} catch (error62) {
|
|
28003
|
+
if (isNotFound(error62)) return void 0;
|
|
28004
|
+
throw error62;
|
|
28005
|
+
}
|
|
28006
|
+
}
|
|
28007
|
+
function holders(root) {
|
|
28008
|
+
const found = [];
|
|
28009
|
+
for (const name of SECTION_FILES) {
|
|
28010
|
+
const path = join8(root, name);
|
|
28011
|
+
const text = readOptional3(path);
|
|
28012
|
+
if (text !== void 0 && findSection(text) !== void 0) {
|
|
28013
|
+
found.push({ path, text, skill: false });
|
|
28014
|
+
}
|
|
28015
|
+
}
|
|
28016
|
+
const skill = join8(root, SKILL_PATH);
|
|
28017
|
+
if (existsSync3(skill)) {
|
|
28018
|
+
found.push({ path: skill, text: readOptional3(skill) ?? "", skill: true });
|
|
28019
|
+
}
|
|
28020
|
+
return found;
|
|
28021
|
+
}
|
|
28022
|
+
function agentOf(lines, path) {
|
|
28023
|
+
const named = agentOfText(lines);
|
|
28024
|
+
if (named !== void 0) {
|
|
28025
|
+
try {
|
|
28026
|
+
return parseAgentName(named);
|
|
28027
|
+
} catch {
|
|
28028
|
+
}
|
|
28029
|
+
}
|
|
28030
|
+
return agentForFile(path);
|
|
28031
|
+
}
|
|
28032
|
+
function rewrite(holder, tags, scope) {
|
|
28033
|
+
if (holder.skill) {
|
|
28034
|
+
const agent2 = agentOf(holder.text.split(/\r?\n/), holder.path);
|
|
28035
|
+
return withLineBreak(skillFile(agentSnippet(tags, { ...scope, agent: agent2 })), holder.text);
|
|
28036
|
+
}
|
|
28037
|
+
const section = findSection(holder.text);
|
|
28038
|
+
if (section === void 0) return holder.text;
|
|
28039
|
+
const agent = agentOf(sectionText(section), holder.path);
|
|
28040
|
+
return replaceSection(section, agentSnippet(tags, { ...scope, agent }));
|
|
28041
|
+
}
|
|
28042
|
+
function sink() {
|
|
28043
|
+
return new Writable2({
|
|
28044
|
+
write(_chunk, _encoding, done) {
|
|
28045
|
+
done();
|
|
28046
|
+
}
|
|
28047
|
+
});
|
|
28048
|
+
}
|
|
28049
|
+
async function runSync(options, contextOf, io) {
|
|
28050
|
+
if (options.quiet !== true) return sync(options, contextOf(), io);
|
|
28051
|
+
const silent = { ...io, stdout: sink(), stderr: sink() };
|
|
28052
|
+
try {
|
|
28053
|
+
const context = contextOf();
|
|
28054
|
+
context.timeoutMs = SYNC_TIMEOUT_MS;
|
|
28055
|
+
const result = await syncFiles({ hook: options.hook }, context, silent);
|
|
28056
|
+
if (result.files.some((file2) => file2.updated)) {
|
|
28057
|
+
writeLine(io.stdout, refreshedLine(result.project, result.tags));
|
|
28058
|
+
}
|
|
28059
|
+
} catch {
|
|
28060
|
+
}
|
|
28061
|
+
return 0;
|
|
28062
|
+
}
|
|
28063
|
+
async function sync(options, context, io) {
|
|
28064
|
+
const result = await syncFiles(options, context, io);
|
|
28065
|
+
if (options.json === true) {
|
|
28066
|
+
writeJson(io.stdout, {
|
|
28067
|
+
project: result.project,
|
|
28068
|
+
tags: result.tags ?? null,
|
|
28069
|
+
files: result.files,
|
|
28070
|
+
hook: result.hook ?? null
|
|
28071
|
+
});
|
|
28072
|
+
return 0;
|
|
28073
|
+
}
|
|
28074
|
+
for (const file2 of result.files) {
|
|
28075
|
+
writeLine(
|
|
28076
|
+
io.stdout,
|
|
28077
|
+
file2.updated ? `Updated ${file2.path}` : `${file2.path} is up to date`
|
|
28078
|
+
);
|
|
28079
|
+
}
|
|
28080
|
+
return 0;
|
|
28081
|
+
}
|
|
28082
|
+
async function syncFiles(options, context, io) {
|
|
28083
|
+
const project = requireProject(context);
|
|
28084
|
+
const root = context.projectRoot ?? io.cwd;
|
|
28085
|
+
let hook;
|
|
28086
|
+
if (options.hook === true) {
|
|
28087
|
+
const plan = planHook(root);
|
|
28088
|
+
const line = applyHook(plan);
|
|
28089
|
+
hook = plan.path;
|
|
28090
|
+
if (options.json !== true) writeLine(io.stdout, line);
|
|
28091
|
+
}
|
|
28092
|
+
const found = holders(root);
|
|
28093
|
+
if (found.length === 0) {
|
|
28094
|
+
throw new UsageError(
|
|
28095
|
+
`no Quibble snippet under ${root}: run quibble snippet --append CLAUDE.md`
|
|
28096
|
+
);
|
|
28097
|
+
}
|
|
28098
|
+
const { tags, scope } = await snippetSource(project, context, io, { queue: false });
|
|
28099
|
+
const files = [];
|
|
28100
|
+
for (const holder of found) {
|
|
28101
|
+
const next = rewrite(holder, tags, scope);
|
|
28102
|
+
const updated = next !== holder.text;
|
|
28103
|
+
if (updated) writeFileSync6(holder.path, next, "utf8");
|
|
28104
|
+
files.push({ path: holder.path, updated });
|
|
28105
|
+
}
|
|
28106
|
+
return { project, tags, files, hook };
|
|
28107
|
+
}
|
|
28108
|
+
|
|
27612
28109
|
// src/commands/tags.ts
|
|
27613
28110
|
async function runTags(options, context, io) {
|
|
27614
28111
|
const project = requireProject(context);
|
|
27615
28112
|
if (context.mode !== "remote") checkActAs(context);
|
|
27616
|
-
const tags = context.mode === "remote" ? (await openOrgClient(context, io).project(project)).tags : context.allowedTags;
|
|
28113
|
+
const tags = context.mode === "remote" ? (await (await openOrgClient(context, io)).project(project)).tags : context.allowedTags;
|
|
27617
28114
|
if (options.json === true) {
|
|
27618
28115
|
writeJson(io.stdout, { project, tags: tags ?? null });
|
|
27619
28116
|
return 0;
|
|
@@ -27634,7 +28131,7 @@ async function runTagsEdit(edit, given, options, context, io) {
|
|
|
27634
28131
|
const project = requireProject(context);
|
|
27635
28132
|
if (given.length === 0) throw new UsageError(`tags ${edit} needs at least one tag`);
|
|
27636
28133
|
const named = parseAllowedTags(given);
|
|
27637
|
-
const client = openOrgClient(context, io);
|
|
28134
|
+
const client = await openOrgClient(context, io);
|
|
27638
28135
|
let next;
|
|
27639
28136
|
if (edit === "set") {
|
|
27640
28137
|
next = named;
|
|
@@ -27695,7 +28192,7 @@ async function runTokenCreate(name, options, context, io, now = /* @__PURE__ */
|
|
|
27695
28192
|
{ name, ...expiresAt === void 0 ? {} : { expiresAt } },
|
|
27696
28193
|
now
|
|
27697
28194
|
);
|
|
27698
|
-
const client = openOrgClient(context, io);
|
|
28195
|
+
const client = await openOrgClient(context, io);
|
|
27699
28196
|
const minted = await client.createToken(project, body);
|
|
27700
28197
|
if (options.json === true) {
|
|
27701
28198
|
writeJson(io.stdout, minted.body);
|
|
@@ -27705,13 +28202,13 @@ async function runTokenCreate(name, options, context, io, now = /* @__PURE__ */
|
|
|
27705
28202
|
io.stdout,
|
|
27706
28203
|
`Created token ${minted.token.name} for ${org}/${project}. It is shown once:`
|
|
27707
28204
|
);
|
|
27708
|
-
writeLine(io.stdout, `QUIBBLE_ENDPOINT=${
|
|
28205
|
+
writeLine(io.stdout, `QUIBBLE_ENDPOINT=${trimSlash2(client.endpoint)}`);
|
|
27709
28206
|
writeLine(io.stdout, `QUIBBLE_TOKEN=${minted.plaintext}`);
|
|
27710
28207
|
return 0;
|
|
27711
28208
|
}
|
|
27712
28209
|
async function runTokenList(options, context, io, now = /* @__PURE__ */ new Date()) {
|
|
27713
28210
|
const { project } = scopeOf(context);
|
|
27714
|
-
const answer2 = await openOrgClient(context, io).tokens(project);
|
|
28211
|
+
const answer2 = await (await openOrgClient(context, io)).tokens(project);
|
|
27715
28212
|
if (options.json === true) {
|
|
27716
28213
|
writeJson(io.stdout, answer2.body);
|
|
27717
28214
|
return 0;
|
|
@@ -27750,7 +28247,7 @@ function findToken(tokens, value) {
|
|
|
27750
28247
|
}
|
|
27751
28248
|
async function runTokenRevoke(value, options, context, io) {
|
|
27752
28249
|
const { project, org } = scopeOf(context);
|
|
27753
|
-
const client = openOrgClient(context, io);
|
|
28250
|
+
const client = await openOrgClient(context, io);
|
|
27754
28251
|
const token = findToken((await client.tokens(project)).tokens, value);
|
|
27755
28252
|
await client.revokeToken(project, token.id);
|
|
27756
28253
|
if (options.json === true) {
|
|
@@ -27785,6 +28282,7 @@ var TAGS_EDITS = [
|
|
|
27785
28282
|
var ID_HELP = `complaint id, or a unique prefix of at least ${SHORT_ID_MIN} characters`;
|
|
27786
28283
|
var REASON_HELP = `${REASONS.slice(0, -1).join(", ")} or ${REASONS.at(-1) ?? ""}`;
|
|
27787
28284
|
function packageVersion() {
|
|
28285
|
+
if (typeof QUIBBLE_BUILD_VERSION === "string") return QUIBBLE_BUILD_VERSION;
|
|
27788
28286
|
const require2 = createRequire(import.meta.url);
|
|
27789
28287
|
const manifest = require2("../package.json");
|
|
27790
28288
|
return typeof manifest.version === "string" ? manifest.version : "0.0.0";
|
|
@@ -27798,7 +28296,7 @@ function rejectEmptyOptions(options) {
|
|
|
27798
28296
|
}
|
|
27799
28297
|
}
|
|
27800
28298
|
function withGlobalOptions(command) {
|
|
27801
|
-
return command.option("--local", "use the local store
|
|
28299
|
+
return command.option("--local", "use the local store, whatever the endpoint says").option("--endpoint <url>", "API URL, web URL, host, or local").option("--org <slug>", "organization to use in remote mode").option("--json", "print JSON instead of text");
|
|
27802
28300
|
}
|
|
27803
28301
|
function globals(program2, command) {
|
|
27804
28302
|
const outer = program2.opts();
|
|
@@ -27836,19 +28334,34 @@ function buildProgram(io, setCode) {
|
|
|
27836
28334
|
}).exitOverride();
|
|
27837
28335
|
withGlobalOptions(program2);
|
|
27838
28336
|
withGlobalOptions(
|
|
27839
|
-
program2.command("init").description("write a .quibblerc in the current directory").option("--project <name>", "project name (default: the directory name)").option("-t, --tag <tag>", "allowed tag (repeatable)", collect).option("--agent <name>", "agent named in the snippet (default: claude)").option("--skill", "also write .claude/skills/quibble/SKILL.md").option("--force", "overwrite an existing .quibblerc or skill file")
|
|
28337
|
+
program2.command("init").description("write a .quibblerc in the current directory").option("--project <name>", "project name (default: the directory name)").option("-t, --tag <tag>", "allowed tag (repeatable)", collect).option("--agent <name>", "agent named in the snippet (default: claude)").option("--skill", "also write .claude/skills/quibble/SKILL.md").option("--hook", "also add the quibble sync hook to .claude/settings.json").option("--force", "overwrite an existing .quibblerc or skill file")
|
|
27840
28338
|
).action(async function() {
|
|
27841
28339
|
const options = { ...this.opts(), ...globals(program2, this) };
|
|
27842
28340
|
rejectEmptyOptions(options);
|
|
27843
28341
|
setCode(await runInit(options, io));
|
|
27844
28342
|
});
|
|
27845
28343
|
withGlobalOptions(
|
|
27846
|
-
program2.command("snippet").description("print or write the agent snippet of this project").option("--append <file>", "append the snippet to a file such as CLAUDE.md").option("--skill", "write .claude/skills/quibble/SKILL.md").option("--agent <name>", "agent named in the snippet").option("--project <name>", "project name (overrides .quibblerc)").option("--force", "overwrite an existing skill file")
|
|
28344
|
+
program2.command("snippet").description("print or write the agent snippet of this project").option("--append <file>", "append the snippet to a file such as CLAUDE.md").option("--skill", "write .claude/skills/quibble/SKILL.md").option("--hook", "add the quibble sync hook to .claude/settings.json").option("--agent <name>", "agent named in the snippet").option("--project <name>", "project name (overrides .quibblerc)").option("--force", "overwrite an existing skill file")
|
|
27847
28345
|
).action(async function() {
|
|
27848
28346
|
const options = { ...this.opts(), ...globals(program2, this) };
|
|
27849
28347
|
rejectEmptyOptions(options);
|
|
27850
28348
|
setCode(await runSnippet(options, contextFor(options, io), io));
|
|
27851
28349
|
});
|
|
28350
|
+
withGlobalOptions(
|
|
28351
|
+
program2.command("sync").description("rewrite the agent snippet in place with the current tags").option("--hook", "also add the quibble sync hook to .claude/settings.json").option("--quiet", "print nothing but a changed tag list, and always exit 0").option("--project <name>", "project name (overrides .quibblerc)")
|
|
28352
|
+
).action(async function() {
|
|
28353
|
+
const options = { ...this.opts(), ...globals(program2, this) };
|
|
28354
|
+
setCode(
|
|
28355
|
+
await runSync(
|
|
28356
|
+
options,
|
|
28357
|
+
() => {
|
|
28358
|
+
rejectEmptyOptions(options);
|
|
28359
|
+
return contextFor(options, io);
|
|
28360
|
+
},
|
|
28361
|
+
io
|
|
28362
|
+
)
|
|
28363
|
+
);
|
|
28364
|
+
});
|
|
27852
28365
|
withGlobalOptions(
|
|
27853
28366
|
program2.command("complain").description("file a complaint").argument("[message]", "what went wrong; omit it or pass `-` to read stdin").option("-s, --severity <level>", "low, medium or high").option("-t, --tag <tag>", "tag (repeatable)", collect).option("--agent <name>", "name of the agent filing it").option("--reporter <name>", "person you are filing for (local mode only)").option("--project <name>", "project name (overrides .quibblerc)").option("--cmd <command>", "the command that failed")
|
|
27854
28367
|
).action(async function(message) {
|
|
@@ -27944,6 +28457,14 @@ function buildProgram(io, setCode) {
|
|
|
27944
28457
|
rejectEmptyOptions(options);
|
|
27945
28458
|
setCode(await runTokenRevoke(value, options, contextFor(options, io), io));
|
|
27946
28459
|
});
|
|
28460
|
+
const projectCommand = program2.command("project").description("manage the projects of the organization (remote only)");
|
|
28461
|
+
withGlobalOptions(
|
|
28462
|
+
projectCommand.command("delete").description("delete a project and hide its complaints").argument("<name>", "project name").option("--yes", "skip the question")
|
|
28463
|
+
).action(async function(name) {
|
|
28464
|
+
const options = { ...this.opts(), ...globals(program2, this) };
|
|
28465
|
+
rejectEmptyOptions(options);
|
|
28466
|
+
setCode(await runProjectDelete(name, options, contextFor(options, io), io));
|
|
28467
|
+
});
|
|
27947
28468
|
withGlobalOptions(
|
|
27948
28469
|
program2.command("retry").description("send the complaints queued while offline")
|
|
27949
28470
|
).action(async function() {
|
|
@@ -27988,10 +28509,10 @@ function buildProgram(io, setCode) {
|
|
|
27988
28509
|
});
|
|
27989
28510
|
withGlobalOptions(
|
|
27990
28511
|
program2.command("config").description("print the resolved configuration")
|
|
27991
|
-
).action(function() {
|
|
28512
|
+
).action(async function() {
|
|
27992
28513
|
const options = { ...this.opts(), ...globals(program2, this) };
|
|
27993
28514
|
rejectEmptyOptions(options);
|
|
27994
|
-
setCode(runConfig(options, contextFor(options, io), io));
|
|
28515
|
+
setCode(await runConfig(options, contextFor(options, io), io));
|
|
27995
28516
|
});
|
|
27996
28517
|
return program2;
|
|
27997
28518
|
}
|
|
@@ -27999,9 +28520,10 @@ function stripErrorPrefix(message) {
|
|
|
27999
28520
|
return message.replace(/^error:\s*/i, "");
|
|
28000
28521
|
}
|
|
28001
28522
|
async function main(argv, io) {
|
|
28002
|
-
|
|
28523
|
+
resetDiscovery();
|
|
28524
|
+
let code2 = 0;
|
|
28003
28525
|
const program2 = buildProgram(io, (value) => {
|
|
28004
|
-
|
|
28526
|
+
code2 = value;
|
|
28005
28527
|
});
|
|
28006
28528
|
if (argv.length === 0) {
|
|
28007
28529
|
io.stdout.write(program2.helpInformation());
|
|
@@ -28009,7 +28531,7 @@ async function main(argv, io) {
|
|
|
28009
28531
|
}
|
|
28010
28532
|
try {
|
|
28011
28533
|
await program2.parseAsync(argv, { from: "user" });
|
|
28012
|
-
return
|
|
28534
|
+
return code2;
|
|
28013
28535
|
} catch (error62) {
|
|
28014
28536
|
if (error62 instanceof CommanderError) {
|
|
28015
28537
|
if (error62.code === "commander.help" || error62.code === "commander.helpDisplayed") {
|
|
@@ -28031,10 +28553,18 @@ for (const stream of [process2.stdout, process2.stderr]) {
|
|
|
28031
28553
|
throw error62;
|
|
28032
28554
|
});
|
|
28033
28555
|
}
|
|
28034
|
-
|
|
28556
|
+
function flushed(stream) {
|
|
28557
|
+
return new Promise((resolve4) => {
|
|
28558
|
+
stream.write("", () => resolve4());
|
|
28559
|
+
});
|
|
28560
|
+
}
|
|
28561
|
+
var code = await main(process2.argv.slice(2), {
|
|
28035
28562
|
stdin: process2.stdin,
|
|
28036
28563
|
stdout: process2.stdout,
|
|
28037
28564
|
stderr: process2.stderr,
|
|
28038
28565
|
env: process2.env,
|
|
28039
28566
|
cwd: process2.cwd()
|
|
28040
28567
|
});
|
|
28568
|
+
process2.exitCode = code;
|
|
28569
|
+
await Promise.all([flushed(process2.stdout), flushed(process2.stderr)]);
|
|
28570
|
+
process2.exit(code);
|