@uru-intelligence/cli 0.3.54 → 0.3.56
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 +8 -0
- package/dist/uru.mjs +491 -269
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -54,6 +54,14 @@ bun src/index.ts gems write 'library/Golden Gems/My Gem.gem' main.html --file ./
|
|
|
54
54
|
bun src/index.ts library ls library
|
|
55
55
|
bun src/index.ts library search revenue
|
|
56
56
|
bun src/index.ts library mkdir 'library/Golden Gems'
|
|
57
|
+
bun src/index.ts library read 'library/Contexts/Client Brief.md'
|
|
58
|
+
bun src/index.ts library write 'library/Contexts/Client Brief.md' --file ./brief.md --create
|
|
59
|
+
bun src/index.ts library write 'library/Prompts/Sales Email.md' --content "Draft the follow-up." --create
|
|
60
|
+
bun src/index.ts library patch 'library/Prompts/Sales Email.md' --stdin
|
|
61
|
+
bun src/index.ts library cp 'library/Notes/Brief.md' 'library/Archive/Brief.md'
|
|
62
|
+
bun src/index.ts library mv 'library/Notes/Draft.md' 'library/Notes/Final.md'
|
|
63
|
+
bun src/index.ts library rm 'library/Notes/Old.md'
|
|
64
|
+
bun src/index.ts library restore 'library/Notes/Old.md'
|
|
57
65
|
bun src/index.ts datasets query revenue_rows --limit 50 --response-format concise --json
|
|
58
66
|
bun src/index.ts datasets get --params-json '{"dataset_id":"revenue_rows"}'
|
|
59
67
|
bun src/index.ts datasets rows-upsert --params-json '{"dataset_id":"revenue_rows","rows":[{"name":"Acme"}]}'
|
package/dist/uru.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { spawn as spawn3 } from "node:child_process";
|
|
|
6
6
|
|
|
7
7
|
// src/types.ts
|
|
8
8
|
var DEFAULT_API_URL = "https://api.uruintelligence.com";
|
|
9
|
-
var CLI_VERSION = "0.3.
|
|
9
|
+
var CLI_VERSION = "0.3.55";
|
|
10
10
|
var ExitCode = {
|
|
11
11
|
Ok: 0,
|
|
12
12
|
Failure: 1,
|
|
@@ -241,8 +241,12 @@ function buildOperationGroups(op) {
|
|
|
241
241
|
const libraryOperations = [
|
|
242
242
|
op("library.ls", "library", "List visible Library folders and resources.", "library_query", "ls", "read"),
|
|
243
243
|
op("library.stat", "library", "Read metadata for a visible Library path.", "library_query", "stat", "read"),
|
|
244
|
+
op("library.read", "library", "Read a visible text-backed Library resource by path.", "library_query", "read", "read"),
|
|
244
245
|
op("library.search", "library", "Search visible Library resources server-side.", "library_query", "search", "read"),
|
|
245
246
|
op("library.mkdir", "library", "Create a Library folder.", "library_fs", "mkdir", "write"),
|
|
247
|
+
op("library.write", "library", "Create or replace a text-backed Library resource by path.", "library_fs", "write", "write"),
|
|
248
|
+
op("library.patch", "library", "Replace text content for a text-backed Library resource by path.", "library_fs", "patch", "write"),
|
|
249
|
+
op("library.cp", "library", "Copy a supported Library item when the backing resource supports copy semantics.", "library_fs", "cp", "write"),
|
|
246
250
|
op("library.mv", "library", "Move or rename a Library item.", "library_fs", "mv", "write"),
|
|
247
251
|
op("library.move_workspace", "library", "Move a Library item to another workspace.", "library_fs", "move_workspace", "destructive"),
|
|
248
252
|
op("library.rm", "library", "Trash a Library item.", "library_fs", "rm", "destructive"),
|
|
@@ -250,10 +254,7 @@ function buildOperationGroups(op) {
|
|
|
250
254
|
op("library.empty_trash", "library", "Permanently empty Library trash.", "library_fs", "empty_trash", "destructive"),
|
|
251
255
|
op("library.upload_preflight", "library", "Preflight a Library upload.", "library_upload", "preflight", "read"),
|
|
252
256
|
op("library.upload_create_session", "library", "Create a direct Library upload session.", "library_upload", "create_session", "write"),
|
|
253
|
-
op("library.upload_complete_session", "library", "Complete a direct Library upload session.", "library_upload", "complete_session", "write")
|
|
254
|
-
op("context.manage", "library", "Manage typed Context Library metadata such as workspace-default, teams, category, and favorite state.", "context_manage", undefined, "write"),
|
|
255
|
-
op("prompt.render", "library", "Render a typed Prompt Library resource with supplied variables.", "prompt_render", undefined, "read"),
|
|
256
|
-
op("persona.query", "library", "Query typed Persona Library resources and linked prompt/context resources.", "persona_query", undefined, "read")
|
|
257
|
+
op("library.upload_complete_session", "library", "Complete a direct Library upload session.", "library_upload", "complete_session", "write")
|
|
257
258
|
];
|
|
258
259
|
const sharingOperations = [
|
|
259
260
|
op("sharing.share", "sharing", "Share a Library resource with a user, team, or workspace.", "library_collab", "share", "write"),
|
|
@@ -416,7 +417,7 @@ function buildOperationGroups(op) {
|
|
|
416
417
|
op("file.download_for_user", "files", "Prepare a file download for the user.", "DOWNLOAD_FILE_FOR_USER", undefined, "read")
|
|
417
418
|
];
|
|
418
419
|
const businessSqlOperations = [
|
|
419
|
-
op("business_sql.transcript", "business_sql", "Query transcript SQL.", "
|
|
420
|
+
op("business_sql.transcript", "business_sql", "Query transcript SQL.", "transcript_sql", undefined, "read"),
|
|
420
421
|
op("business_sql.maguire", "business_sql", "Query Maguire SQL.", "MAGUIRE_SQL", undefined, "read"),
|
|
421
422
|
op("business_sql.tyler", "business_sql", "Query Tyler SQL.", "TYLER_SQL", undefined, "read"),
|
|
422
423
|
op("business_sql.cfo", "business_sql", "Query CFO SQL.", "CFO_SQL", undefined, "read"),
|
|
@@ -473,7 +474,6 @@ var internalOnlyPlatformToolIds = new Set([
|
|
|
473
474
|
"MAGUIRE_WRITE_SQL",
|
|
474
475
|
"PEREGRINE_SQL",
|
|
475
476
|
"RESTORE_DATASET_ROWS",
|
|
476
|
-
"TRANSCRIPT_SQL",
|
|
477
477
|
"TYLER_SQL",
|
|
478
478
|
"UPSERT_DATASET_ROWS"
|
|
479
479
|
]);
|
|
@@ -906,171 +906,6 @@ function normalizeTool(value) {
|
|
|
906
906
|
return [tool];
|
|
907
907
|
}
|
|
908
908
|
|
|
909
|
-
// src/cli-helpers.ts
|
|
910
|
-
function parseApiArgs(args) {
|
|
911
|
-
let method = "GET";
|
|
912
|
-
let path;
|
|
913
|
-
let body;
|
|
914
|
-
const fields = {};
|
|
915
|
-
for (let index = 0;index < args.length; index += 1) {
|
|
916
|
-
const arg = args[index];
|
|
917
|
-
if (arg === undefined) {
|
|
918
|
-
continue;
|
|
919
|
-
}
|
|
920
|
-
if (arg === "-X" || arg === "--method") {
|
|
921
|
-
method = requireCommandValue(args[index + 1], `${arg} requires a method`).toUpperCase();
|
|
922
|
-
index += 1;
|
|
923
|
-
continue;
|
|
924
|
-
}
|
|
925
|
-
if (arg.startsWith("-X") && arg.length > 2) {
|
|
926
|
-
method = arg.slice(2).toUpperCase();
|
|
927
|
-
continue;
|
|
928
|
-
}
|
|
929
|
-
if (arg === "--body-json" || arg === "--data-json") {
|
|
930
|
-
body = parseParamsJson(requireCommandValue(args[index + 1], `${arg} requires a JSON object`), arg);
|
|
931
|
-
index += 1;
|
|
932
|
-
continue;
|
|
933
|
-
}
|
|
934
|
-
if (arg === "-F" || arg === "--field") {
|
|
935
|
-
addApiField(fields, requireCommandValue(args[index + 1], `${arg} requires key=value`));
|
|
936
|
-
index += 1;
|
|
937
|
-
continue;
|
|
938
|
-
}
|
|
939
|
-
if (arg.startsWith("-F") && arg.length > 2) {
|
|
940
|
-
addApiField(fields, arg.slice(2));
|
|
941
|
-
continue;
|
|
942
|
-
}
|
|
943
|
-
if (path === undefined) {
|
|
944
|
-
path = normalizeApiPath(arg);
|
|
945
|
-
continue;
|
|
946
|
-
}
|
|
947
|
-
throw new CliError(`Unexpected api argument: ${arg}`);
|
|
948
|
-
}
|
|
949
|
-
if (Object.keys(fields).length > 0) {
|
|
950
|
-
body = { ...body ?? {}, ...fields };
|
|
951
|
-
if (method === "GET") {
|
|
952
|
-
method = "POST";
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
return {
|
|
956
|
-
path: requireCommandValue(path, "Usage: uru api <path> [-X POST] [-F k=v] [--body-json {...}]"),
|
|
957
|
-
method,
|
|
958
|
-
...body === undefined ? {} : { body }
|
|
959
|
-
};
|
|
960
|
-
}
|
|
961
|
-
function normalizeApiPath(value) {
|
|
962
|
-
const trimmed = value.trim();
|
|
963
|
-
if (trimmed.startsWith("/")) {
|
|
964
|
-
return trimmed;
|
|
965
|
-
}
|
|
966
|
-
return `/api/${trimmed.replace(/^api\//, "")}`;
|
|
967
|
-
}
|
|
968
|
-
function addApiField(target, raw) {
|
|
969
|
-
const equals = raw.indexOf("=");
|
|
970
|
-
if (equals <= 0) {
|
|
971
|
-
throw new CliError("-F/--field requires key=value");
|
|
972
|
-
}
|
|
973
|
-
target[raw.slice(0, equals)] = raw.slice(equals + 1);
|
|
974
|
-
}
|
|
975
|
-
function paramsJson(args) {
|
|
976
|
-
for (let index = 0;index < args.length; index += 1) {
|
|
977
|
-
const arg = args[index];
|
|
978
|
-
if (arg === "--params-json" || arg === "--args-json") {
|
|
979
|
-
const value = args[index + 1];
|
|
980
|
-
if (value === undefined) {
|
|
981
|
-
throw new CliError(`${arg} requires a JSON object`);
|
|
982
|
-
}
|
|
983
|
-
return parseParamsJson(value, arg);
|
|
984
|
-
}
|
|
985
|
-
if (arg?.startsWith("--params-json=")) {
|
|
986
|
-
return parseParamsJson(arg.slice("--params-json=".length), "--params-json");
|
|
987
|
-
}
|
|
988
|
-
if (arg?.startsWith("--args-json=")) {
|
|
989
|
-
return parseParamsJson(arg.slice("--args-json=".length), "--args-json");
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
return {};
|
|
993
|
-
}
|
|
994
|
-
function parseParamsJson(value, label) {
|
|
995
|
-
try {
|
|
996
|
-
return parseJsonObject(value, label);
|
|
997
|
-
} catch (error) {
|
|
998
|
-
throw new CliError(error instanceof Error ? error.message : `Invalid ${label}`, {
|
|
999
|
-
cause: error
|
|
1000
|
-
});
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
function parseLocalFlags(args) {
|
|
1004
|
-
const values = {};
|
|
1005
|
-
const booleans = new Set;
|
|
1006
|
-
const positionals = [];
|
|
1007
|
-
for (let index = 0;index < args.length; index += 1) {
|
|
1008
|
-
const arg = args[index];
|
|
1009
|
-
if (arg === undefined) {
|
|
1010
|
-
continue;
|
|
1011
|
-
}
|
|
1012
|
-
if (!arg.startsWith("--")) {
|
|
1013
|
-
positionals.push(arg);
|
|
1014
|
-
continue;
|
|
1015
|
-
}
|
|
1016
|
-
const equals = arg.indexOf("=");
|
|
1017
|
-
if (equals > -1) {
|
|
1018
|
-
values[arg.slice(0, equals)] = arg.slice(equals + 1);
|
|
1019
|
-
continue;
|
|
1020
|
-
}
|
|
1021
|
-
const next = args[index + 1];
|
|
1022
|
-
if (next !== undefined && !next.startsWith("--")) {
|
|
1023
|
-
values[arg] = next;
|
|
1024
|
-
index += 1;
|
|
1025
|
-
} else {
|
|
1026
|
-
booleans.add(arg);
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
return { values, booleans, positionals };
|
|
1030
|
-
}
|
|
1031
|
-
function integerFlag(flags, name, fallback) {
|
|
1032
|
-
const raw = flags.values[name];
|
|
1033
|
-
if (raw === undefined) {
|
|
1034
|
-
return fallback;
|
|
1035
|
-
}
|
|
1036
|
-
const value = Number.parseInt(raw, 10);
|
|
1037
|
-
if (!Number.isFinite(value) || value < 0) {
|
|
1038
|
-
throw new CliError(`${name} must be a non-negative integer`);
|
|
1039
|
-
}
|
|
1040
|
-
return value;
|
|
1041
|
-
}
|
|
1042
|
-
function copyStringFlag(flags, target, flagName, paramName) {
|
|
1043
|
-
const value = flags.values[flagName];
|
|
1044
|
-
if (value !== undefined) {
|
|
1045
|
-
target[paramName] = value;
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
function isJsonRequested(argv) {
|
|
1049
|
-
for (let index = 0;index < argv.length; index += 1) {
|
|
1050
|
-
const arg = argv[index];
|
|
1051
|
-
if (arg === "--json") {
|
|
1052
|
-
return true;
|
|
1053
|
-
}
|
|
1054
|
-
if (arg === "--output-format=json" || arg === "--output-format=stream-json" || arg === "--output=json" || arg === "--output=stream-json") {
|
|
1055
|
-
return true;
|
|
1056
|
-
}
|
|
1057
|
-
if (arg === "--output-format" || arg === "--output") {
|
|
1058
|
-
const value = argv[index + 1];
|
|
1059
|
-
if (value === "json" || value === "stream-json") {
|
|
1060
|
-
return true;
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
return false;
|
|
1065
|
-
}
|
|
1066
|
-
function nonEmptyString(value) {
|
|
1067
|
-
if (value === undefined) {
|
|
1068
|
-
return;
|
|
1069
|
-
}
|
|
1070
|
-
const trimmed = value.trim();
|
|
1071
|
-
return trimmed === "" ? undefined : trimmed;
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
909
|
// src/output.ts
|
|
1075
910
|
function writeJson(io, value) {
|
|
1076
911
|
io.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
@@ -1184,6 +1019,12 @@ LIBRARY / DATASETS / TOOLS
|
|
|
1184
1019
|
uru library ls [path] List Library items
|
|
1185
1020
|
uru library search <query> Search Library items
|
|
1186
1021
|
uru library mkdir <path> Create a Library folder
|
|
1022
|
+
uru library read <path> Print a text-backed Library resource
|
|
1023
|
+
uru library write <path> --file ./note.md --create Create/replace text-backed Library content
|
|
1024
|
+
uru library patch <path> --content "..." Replace text-backed Library content
|
|
1025
|
+
uru library cp|mv <path> <target-path> Copy or move visible Library items
|
|
1026
|
+
uru library rm <path> [--permanent --yes] Trash or permanently delete Library items
|
|
1027
|
+
uru library restore <path> Restore a trashed Library item
|
|
1187
1028
|
# Prompts, personas, contexts: use typed Library paths such as library/Prompts/Foo.prompt.md
|
|
1188
1029
|
uru datasets query <dataset-slug> [--limit 50] Query dataset rows; add --response-format concise for compact output
|
|
1189
1030
|
uru automations ls|get|create|enable|disable Manage automations without raw JSON parameters
|
|
@@ -1400,8 +1241,20 @@ the check a release gate; security also accepts --deep.
|
|
|
1400
1241
|
library: `Usage: uru library ls [path]
|
|
1401
1242
|
uru library search <query>
|
|
1402
1243
|
uru library mkdir <path>
|
|
1244
|
+
uru library read <path>
|
|
1245
|
+
uru library write <path> (--file <local-path>|--content <text>|--stdin) [--create]
|
|
1246
|
+
uru library patch <path> (--file <local-path>|--content <text>|--stdin)
|
|
1247
|
+
uru library cp <path> <target-path>
|
|
1248
|
+
uru library mv <path> <target-path>
|
|
1249
|
+
uru library rm <path> [--permanent] [--yes]
|
|
1250
|
+
uru library restore <path>
|
|
1251
|
+
uru library empty-trash --yes
|
|
1403
1252
|
|
|
1404
|
-
Operate on Library items through platform tools.
|
|
1253
|
+
Operate on Library items through platform tools. Use write --create to create
|
|
1254
|
+
text-backed Context, Prompt, Persona, note, and plain .md/.txt resources at
|
|
1255
|
+
visible Library paths. For binary files, use library_upload through
|
|
1256
|
+
\`uru operations run\` or \`uru tools run\`; for Gem child source files, use
|
|
1257
|
+
\`uru gems read|write\`.
|
|
1405
1258
|
`,
|
|
1406
1259
|
datasets: `Usage: uru datasets query <dataset-slug> [--limit 50] [--offset 0] [--response-format detailed|concise]
|
|
1407
1260
|
|
|
@@ -1563,7 +1416,14 @@ async function executeOperationStream(ctx, operationId, params, onEvent) {
|
|
|
1563
1416
|
if (operation.backendOp !== undefined) {
|
|
1564
1417
|
toolParams["op"] = operation.backendOp;
|
|
1565
1418
|
}
|
|
1566
|
-
|
|
1419
|
+
const response = await client(ctx).streamTool(operation.backendToolName, toolParams, (event) => {
|
|
1420
|
+
assertToolResultSuccess(event);
|
|
1421
|
+
onEvent(event);
|
|
1422
|
+
});
|
|
1423
|
+
if (response.payload !== undefined) {
|
|
1424
|
+
assertToolResultSuccess(response.payload);
|
|
1425
|
+
}
|
|
1426
|
+
return response;
|
|
1567
1427
|
}
|
|
1568
1428
|
async function executeOperation(ctx, operationId, params) {
|
|
1569
1429
|
const operation = getPlatformOperation(operationId);
|
|
@@ -1574,7 +1434,9 @@ async function executeOperation(ctx, operationId, params) {
|
|
|
1574
1434
|
if (operation.backendOp !== undefined) {
|
|
1575
1435
|
toolParams["op"] = operation.backendOp;
|
|
1576
1436
|
}
|
|
1577
|
-
|
|
1437
|
+
const result = await client(ctx).runTool(operation.backendToolName, toolParams);
|
|
1438
|
+
assertToolResultSuccess(result);
|
|
1439
|
+
return result;
|
|
1578
1440
|
}
|
|
1579
1441
|
function emit(ctx, value, text) {
|
|
1580
1442
|
if (ctx.outputFormat === "stream-json") {
|
|
@@ -1586,19 +1448,51 @@ function emit(ctx, value, text) {
|
|
|
1586
1448
|
}
|
|
1587
1449
|
}
|
|
1588
1450
|
function unwrapToolResult(value) {
|
|
1589
|
-
|
|
1590
|
-
|
|
1451
|
+
assertToolResultSuccess(value);
|
|
1452
|
+
if (isJsonObject2(value) && Object.hasOwn(value, "result") && (isToolResultEnvelope(value) || isJsonObject2(value["result"]))) {
|
|
1453
|
+
const result = value["result"];
|
|
1454
|
+
if (result !== undefined) {
|
|
1455
|
+
return result;
|
|
1456
|
+
}
|
|
1591
1457
|
}
|
|
1592
1458
|
return value;
|
|
1593
1459
|
}
|
|
1460
|
+
function assertToolResultSuccess(value) {
|
|
1461
|
+
if (!isJsonObject2(value) || !isToolResultEnvelope(value)) {
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
const httpStatus = toolResultHttpStatus(value);
|
|
1465
|
+
const failed = value["success"] === false || Object.hasOwn(value, "error") || Object.hasOwn(value, "errorMessage") || Object.hasOwn(value, "error_message") || Object.hasOwn(value, "errorCode") || Object.hasOwn(value, "error_code") || Object.hasOwn(value, "code") || httpStatus !== undefined && httpStatus >= 400;
|
|
1466
|
+
if (!failed) {
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
const errorCode = stringField(value, ["errorCode", "error_code", "code"]);
|
|
1470
|
+
const toolId = stringField(value, ["toolId", "tool_id"]);
|
|
1471
|
+
const message = stringField(value, ["errorMessage", "error_message", "error", "message"]) ?? `${toolId === undefined ? "Tool operation" : `Tool ${toolId}`} failed`;
|
|
1472
|
+
const details = [
|
|
1473
|
+
errorCode === undefined ? undefined : `[${errorCode}]`,
|
|
1474
|
+
httpStatus === undefined ? undefined : `(HTTP ${httpStatus})`
|
|
1475
|
+
].filter((detail) => detail !== undefined);
|
|
1476
|
+
const tip = stringField(value, ["tip"]);
|
|
1477
|
+
throw new CliError(`${message}${details.length === 0 ? "" : ` ${details.join(" ")}`}${tip === undefined ? "" : ` ${tip}`}`, {
|
|
1478
|
+
code: httpStatus === 409 ? ExitCode.Conflict : httpStatus === 401 || httpStatus === 403 ? ExitCode.AuthRequired : ExitCode.Failure,
|
|
1479
|
+
...errorCode === undefined ? {} : { errorCode }
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
function isToolResultEnvelope(value) {
|
|
1483
|
+
return typeof value["success"] === "boolean" && (typeof value["toolId"] === "string" || typeof value["tool_id"] === "string" || typeof value["executionTimeMs"] === "number" || typeof value["execution_time_ms"] === "number");
|
|
1484
|
+
}
|
|
1485
|
+
function toolResultHttpStatus(value) {
|
|
1486
|
+
const status = value["httpStatus"] ?? value["http_status"];
|
|
1487
|
+
return typeof status === "number" && Number.isFinite(status) ? status : undefined;
|
|
1488
|
+
}
|
|
1594
1489
|
function delegatedPayload(value) {
|
|
1595
1490
|
if (isJsonObject2(value) && isJsonObject2(value["payload"])) {
|
|
1596
1491
|
return value["payload"];
|
|
1597
1492
|
}
|
|
1598
1493
|
return value;
|
|
1599
1494
|
}
|
|
1600
|
-
function gemPathFromArgs(
|
|
1601
|
-
const flags = parseLocalFlags(args);
|
|
1495
|
+
function gemPathFromArgs(flags, linkedProject) {
|
|
1602
1496
|
return flags.values["--path"] ?? flags.values["--gem"] ?? flags.positionals.find((value) => value.startsWith("library/")) ?? linkedProject.libraryPath ?? linkedProject.gemId ?? (() => {
|
|
1603
1497
|
throw new CliError("Gem path required. Pass library/<name>.gem or run `uru link --path library/<name>.gem`.");
|
|
1604
1498
|
})();
|
|
@@ -1822,16 +1716,9 @@ function createMacosKeychainCredentialStore(runCommand) {
|
|
|
1822
1716
|
},
|
|
1823
1717
|
async writeToken(apiUrl, token) {
|
|
1824
1718
|
try {
|
|
1825
|
-
await runCommand("security", [
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
accountForApiUrl(apiUrl),
|
|
1829
|
-
"-s",
|
|
1830
|
-
KEYCHAIN_SERVICE,
|
|
1831
|
-
"-w",
|
|
1832
|
-
token,
|
|
1833
|
-
"-U"
|
|
1834
|
-
]);
|
|
1719
|
+
await runCommand("security", ["-i"], {
|
|
1720
|
+
input: macosKeychainWriteCommand(apiUrl, token)
|
|
1721
|
+
});
|
|
1835
1722
|
} catch (error) {
|
|
1836
1723
|
throw credentialStoreError("macOS Keychain", error);
|
|
1837
1724
|
}
|
|
@@ -1853,6 +1740,11 @@ function createMacosKeychainCredentialStore(runCommand) {
|
|
|
1853
1740
|
}
|
|
1854
1741
|
};
|
|
1855
1742
|
}
|
|
1743
|
+
function macosKeychainWriteCommand(apiUrl, token) {
|
|
1744
|
+
const tokenHex = Buffer.from(token, "utf8").toString("hex");
|
|
1745
|
+
return `add-generic-password -a "${accountForApiUrl(apiUrl)}" -s "${KEYCHAIN_SERVICE}" -X ${tokenHex} -U
|
|
1746
|
+
`;
|
|
1747
|
+
}
|
|
1856
1748
|
function createLinuxSecretToolCredentialStore(runCommand) {
|
|
1857
1749
|
return {
|
|
1858
1750
|
async readToken(apiUrl) {
|
|
@@ -2351,7 +2243,8 @@ var SKIPPED_SOURCE_PATH_SEGMENTS = new Set([
|
|
|
2351
2243
|
"build",
|
|
2352
2244
|
"coverage",
|
|
2353
2245
|
"tmp",
|
|
2354
|
-
"temp"
|
|
2246
|
+
"temp",
|
|
2247
|
+
"__MACOSX"
|
|
2355
2248
|
]);
|
|
2356
2249
|
var SKIPPED_SOURCE_FILE_NAMES = new Set([
|
|
2357
2250
|
".DS_Store",
|
|
@@ -2364,7 +2257,8 @@ var SKIPPED_SOURCE_FILE_NAMES = new Set([
|
|
|
2364
2257
|
]);
|
|
2365
2258
|
function shouldSkipProjectPath(path) {
|
|
2366
2259
|
const segments = path.split("/");
|
|
2367
|
-
|
|
2260
|
+
const name = segments.at(-1) ?? "";
|
|
2261
|
+
return segments.some((segment) => SKIPPED_SOURCE_PATH_SEGMENTS.has(segment)) || SKIPPED_SOURCE_FILE_NAMES.has(name) || name.startsWith("._");
|
|
2368
2262
|
}
|
|
2369
2263
|
function stableStringRecord(record) {
|
|
2370
2264
|
return Object.fromEntries(Object.entries(record).filter(([, value]) => typeof value === "string" && value.trim() !== "").sort(([left], [right]) => compareStrings(left, right)));
|
|
@@ -2419,11 +2313,12 @@ async function maybeNotifyCliUpdate(ctx, options = {}) {
|
|
|
2419
2313
|
if (!shouldCheckForUpdate(ctx, options.env ?? process.env)) {
|
|
2420
2314
|
return;
|
|
2421
2315
|
}
|
|
2316
|
+
const currentConfig = await readConfigBestEffort(ctx);
|
|
2422
2317
|
const now = options.now?.() ?? Date.now();
|
|
2423
|
-
if (!isUpdateCheckDue(
|
|
2318
|
+
if (!isUpdateCheckDue(currentConfig, now)) {
|
|
2424
2319
|
return;
|
|
2425
2320
|
}
|
|
2426
|
-
const checkedConfig = withUpdateCheckTimestamp(
|
|
2321
|
+
const checkedConfig = withUpdateCheckTimestamp(currentConfig, now);
|
|
2427
2322
|
await writeConfigBestEffort(ctx, checkedConfig);
|
|
2428
2323
|
const latestVersion = await fetchLatestVersion({
|
|
2429
2324
|
fetchImpl: ctx.fetch ?? fetch,
|
|
@@ -2445,6 +2340,13 @@ async function maybeNotifyCliUpdate(ctx, options = {}) {
|
|
|
2445
2340
|
ctx.io.stderr.write(`A newer Uru CLI is available: ${CLI_VERSION} → ${latestVersion}. ` + `Run npm install -g ${PACKAGE_NAME}@latest
|
|
2446
2341
|
`);
|
|
2447
2342
|
}
|
|
2343
|
+
async function readConfigBestEffort(ctx) {
|
|
2344
|
+
try {
|
|
2345
|
+
return await ctx.store.read();
|
|
2346
|
+
} catch {
|
|
2347
|
+
return ctx.config;
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2448
2350
|
function shouldCheckForUpdate(ctx, env) {
|
|
2449
2351
|
return ctx.outputFormat === "text" && ctx.terminal.stderrIsTty && !ctx.terminal.ci && env["NO_UPDATE_NOTIFIER"] === undefined && env["URU_NO_UPDATE_NOTIFIER"] === undefined;
|
|
2450
2352
|
}
|
|
@@ -2524,6 +2426,194 @@ function isRecord2(value) {
|
|
|
2524
2426
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2525
2427
|
}
|
|
2526
2428
|
|
|
2429
|
+
// src/cli-helpers.ts
|
|
2430
|
+
var BOOLEAN_LOCAL_FLAGS = new Set([
|
|
2431
|
+
"--blocking",
|
|
2432
|
+
"--create",
|
|
2433
|
+
"--deep",
|
|
2434
|
+
"--dry-run",
|
|
2435
|
+
"--follow",
|
|
2436
|
+
"--force",
|
|
2437
|
+
"--force-build",
|
|
2438
|
+
"--hard-delete",
|
|
2439
|
+
"--no-create",
|
|
2440
|
+
"--no-publish",
|
|
2441
|
+
"--no-wait",
|
|
2442
|
+
"--permanent",
|
|
2443
|
+
"--prod",
|
|
2444
|
+
"--skip-promote",
|
|
2445
|
+
"--stdin",
|
|
2446
|
+
"--value-stdin",
|
|
2447
|
+
"--yes"
|
|
2448
|
+
]);
|
|
2449
|
+
function parseApiArgs(args) {
|
|
2450
|
+
let method = "GET";
|
|
2451
|
+
let path;
|
|
2452
|
+
let body;
|
|
2453
|
+
const fields = {};
|
|
2454
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
2455
|
+
const arg = args[index];
|
|
2456
|
+
if (arg === undefined) {
|
|
2457
|
+
continue;
|
|
2458
|
+
}
|
|
2459
|
+
if (arg === "-X" || arg === "--method") {
|
|
2460
|
+
method = requireCommandValue(args[index + 1], `${arg} requires a method`).toUpperCase();
|
|
2461
|
+
index += 1;
|
|
2462
|
+
continue;
|
|
2463
|
+
}
|
|
2464
|
+
if (arg.startsWith("-X") && arg.length > 2) {
|
|
2465
|
+
method = arg.slice(2).toUpperCase();
|
|
2466
|
+
continue;
|
|
2467
|
+
}
|
|
2468
|
+
if (arg === "--body-json" || arg === "--data-json") {
|
|
2469
|
+
body = parseParamsJson(requireCommandValue(args[index + 1], `${arg} requires a JSON object`), arg);
|
|
2470
|
+
index += 1;
|
|
2471
|
+
continue;
|
|
2472
|
+
}
|
|
2473
|
+
if (arg === "-F" || arg === "--field") {
|
|
2474
|
+
addApiField(fields, requireCommandValue(args[index + 1], `${arg} requires key=value`));
|
|
2475
|
+
index += 1;
|
|
2476
|
+
continue;
|
|
2477
|
+
}
|
|
2478
|
+
if (arg.startsWith("-F") && arg.length > 2) {
|
|
2479
|
+
addApiField(fields, arg.slice(2));
|
|
2480
|
+
continue;
|
|
2481
|
+
}
|
|
2482
|
+
if (path === undefined) {
|
|
2483
|
+
path = normalizeApiPath(arg);
|
|
2484
|
+
continue;
|
|
2485
|
+
}
|
|
2486
|
+
throw new CliError(`Unexpected api argument: ${arg}`);
|
|
2487
|
+
}
|
|
2488
|
+
if (Object.keys(fields).length > 0) {
|
|
2489
|
+
body = { ...body ?? {}, ...fields };
|
|
2490
|
+
if (method === "GET") {
|
|
2491
|
+
method = "POST";
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
return {
|
|
2495
|
+
path: requireCommandValue(path, "Usage: uru api <path> [-X POST] [-F k=v] [--body-json {...}]"),
|
|
2496
|
+
method,
|
|
2497
|
+
...body === undefined ? {} : { body }
|
|
2498
|
+
};
|
|
2499
|
+
}
|
|
2500
|
+
function normalizeApiPath(value) {
|
|
2501
|
+
const trimmed = value.trim();
|
|
2502
|
+
if (trimmed.startsWith("/")) {
|
|
2503
|
+
return trimmed;
|
|
2504
|
+
}
|
|
2505
|
+
return `/api/${trimmed.replace(/^api\//, "")}`;
|
|
2506
|
+
}
|
|
2507
|
+
function addApiField(target, raw) {
|
|
2508
|
+
const equals = raw.indexOf("=");
|
|
2509
|
+
if (equals <= 0) {
|
|
2510
|
+
throw new CliError("-F/--field requires key=value");
|
|
2511
|
+
}
|
|
2512
|
+
target[raw.slice(0, equals)] = raw.slice(equals + 1);
|
|
2513
|
+
}
|
|
2514
|
+
function paramsJson(args) {
|
|
2515
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
2516
|
+
const arg = args[index];
|
|
2517
|
+
if (arg === "--params-json" || arg === "--args-json") {
|
|
2518
|
+
const value = args[index + 1];
|
|
2519
|
+
if (value === undefined) {
|
|
2520
|
+
throw new CliError(`${arg} requires a JSON object`);
|
|
2521
|
+
}
|
|
2522
|
+
return parseParamsJson(value, arg);
|
|
2523
|
+
}
|
|
2524
|
+
if (arg?.startsWith("--params-json=")) {
|
|
2525
|
+
return parseParamsJson(arg.slice("--params-json=".length), "--params-json");
|
|
2526
|
+
}
|
|
2527
|
+
if (arg?.startsWith("--args-json=")) {
|
|
2528
|
+
return parseParamsJson(arg.slice("--args-json=".length), "--args-json");
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
return {};
|
|
2532
|
+
}
|
|
2533
|
+
function parseParamsJson(value, label) {
|
|
2534
|
+
try {
|
|
2535
|
+
return parseJsonObject(value, label);
|
|
2536
|
+
} catch (error) {
|
|
2537
|
+
throw new CliError(error instanceof Error ? error.message : `Invalid ${label}`, {
|
|
2538
|
+
cause: error
|
|
2539
|
+
});
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
function parseLocalFlags(args) {
|
|
2543
|
+
const values = {};
|
|
2544
|
+
const booleans = new Set;
|
|
2545
|
+
const positionals = [];
|
|
2546
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
2547
|
+
const arg = args[index];
|
|
2548
|
+
if (arg === undefined) {
|
|
2549
|
+
continue;
|
|
2550
|
+
}
|
|
2551
|
+
if (!arg.startsWith("--")) {
|
|
2552
|
+
positionals.push(arg);
|
|
2553
|
+
continue;
|
|
2554
|
+
}
|
|
2555
|
+
const equals = arg.indexOf("=");
|
|
2556
|
+
if (equals > -1) {
|
|
2557
|
+
values[arg.slice(0, equals)] = arg.slice(equals + 1);
|
|
2558
|
+
continue;
|
|
2559
|
+
}
|
|
2560
|
+
if (BOOLEAN_LOCAL_FLAGS.has(arg)) {
|
|
2561
|
+
booleans.add(arg);
|
|
2562
|
+
continue;
|
|
2563
|
+
}
|
|
2564
|
+
const next = args[index + 1];
|
|
2565
|
+
if (next !== undefined && !next.startsWith("--")) {
|
|
2566
|
+
values[arg] = next;
|
|
2567
|
+
index += 1;
|
|
2568
|
+
} else {
|
|
2569
|
+
booleans.add(arg);
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
return { values, booleans, positionals };
|
|
2573
|
+
}
|
|
2574
|
+
function integerFlag(flags, name, fallback) {
|
|
2575
|
+
const raw = flags.values[name];
|
|
2576
|
+
if (raw === undefined) {
|
|
2577
|
+
return fallback;
|
|
2578
|
+
}
|
|
2579
|
+
const value = Number.parseInt(raw, 10);
|
|
2580
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
2581
|
+
throw new CliError(`${name} must be a non-negative integer`);
|
|
2582
|
+
}
|
|
2583
|
+
return value;
|
|
2584
|
+
}
|
|
2585
|
+
function copyStringFlag(flags, target, flagName, paramName) {
|
|
2586
|
+
const value = flags.values[flagName];
|
|
2587
|
+
if (value !== undefined) {
|
|
2588
|
+
target[paramName] = value;
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
function isJsonRequested(argv) {
|
|
2592
|
+
for (let index = 0;index < argv.length; index += 1) {
|
|
2593
|
+
const arg = argv[index];
|
|
2594
|
+
if (arg === "--json") {
|
|
2595
|
+
return true;
|
|
2596
|
+
}
|
|
2597
|
+
if (arg === "--output-format=json" || arg === "--output-format=stream-json" || arg === "--output=json" || arg === "--output=stream-json") {
|
|
2598
|
+
return true;
|
|
2599
|
+
}
|
|
2600
|
+
if (arg === "--output-format" || arg === "--output") {
|
|
2601
|
+
const value = argv[index + 1];
|
|
2602
|
+
if (value === "json" || value === "stream-json") {
|
|
2603
|
+
return true;
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
return false;
|
|
2608
|
+
}
|
|
2609
|
+
function nonEmptyString(value) {
|
|
2610
|
+
if (value === undefined) {
|
|
2611
|
+
return;
|
|
2612
|
+
}
|
|
2613
|
+
const trimmed = value.trim();
|
|
2614
|
+
return trimmed === "" ? undefined : trimmed;
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2527
2617
|
// src/auth-commands.ts
|
|
2528
2618
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
2529
2619
|
import { createServer } from "node:http";
|
|
@@ -3184,8 +3274,8 @@ async function switchWorkspace(ctx, workspaceId) {
|
|
|
3184
3274
|
}
|
|
3185
3275
|
|
|
3186
3276
|
// src/local-commands.ts
|
|
3187
|
-
import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
|
|
3188
|
-
import { dirname as dirname3 } from "node:path";
|
|
3277
|
+
import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
|
|
3278
|
+
import { dirname as dirname3, resolve as resolve2 } from "node:path";
|
|
3189
3279
|
var GENERIC_FAMILY_COMMANDS = {
|
|
3190
3280
|
workspace: "workspace",
|
|
3191
3281
|
sharing: "sharing",
|
|
@@ -3414,7 +3504,109 @@ async function dispatchLibrary(ctx, sub, args) {
|
|
|
3414
3504
|
});
|
|
3415
3505
|
return;
|
|
3416
3506
|
}
|
|
3417
|
-
|
|
3507
|
+
if (sub === "read" || sub === "cat") {
|
|
3508
|
+
await emitTool(ctx, "library.read", {
|
|
3509
|
+
path: requireCommandValue(args[0], "Usage: uru library read <path>")
|
|
3510
|
+
}, { textExtractor: extractLibraryTextContent });
|
|
3511
|
+
return;
|
|
3512
|
+
}
|
|
3513
|
+
if (sub === "write") {
|
|
3514
|
+
const flags = parseLocalFlags(args);
|
|
3515
|
+
const path = requireCommandValue(flags.positionals[0], "Usage: uru library write <path> (--file <local-path>|--content <text>|--stdin) [--create]");
|
|
3516
|
+
await emitTool(ctx, "library.write", {
|
|
3517
|
+
path,
|
|
3518
|
+
content: await libraryTextFromFlags(ctx, flags, "write"),
|
|
3519
|
+
create: flags.booleans.has("--create")
|
|
3520
|
+
});
|
|
3521
|
+
return;
|
|
3522
|
+
}
|
|
3523
|
+
if (sub === "patch") {
|
|
3524
|
+
const flags = parseLocalFlags(args);
|
|
3525
|
+
const path = requireCommandValue(flags.positionals[0], "Usage: uru library patch <path> (--file <local-path>|--content <text>|--stdin)");
|
|
3526
|
+
await emitTool(ctx, "library.patch", {
|
|
3527
|
+
path,
|
|
3528
|
+
content: await libraryTextFromFlags(ctx, flags, "patch")
|
|
3529
|
+
});
|
|
3530
|
+
return;
|
|
3531
|
+
}
|
|
3532
|
+
if (sub === "cp" || sub === "copy") {
|
|
3533
|
+
const [path, targetPath] = args;
|
|
3534
|
+
await emitTool(ctx, "library.cp", {
|
|
3535
|
+
path: requireCommandValue(path, "Usage: uru library cp <path> <target-path>"),
|
|
3536
|
+
target_path: requireCommandValue(targetPath, "Usage: uru library cp <path> <target-path>")
|
|
3537
|
+
});
|
|
3538
|
+
return;
|
|
3539
|
+
}
|
|
3540
|
+
if (sub === "mv" || sub === "move" || sub === "rename") {
|
|
3541
|
+
const [path, targetPath] = args;
|
|
3542
|
+
await emitTool(ctx, "library.mv", {
|
|
3543
|
+
path: requireCommandValue(path, "Usage: uru library mv <path> <target-path>"),
|
|
3544
|
+
target_path: requireCommandValue(targetPath, "Usage: uru library mv <path> <target-path>")
|
|
3545
|
+
});
|
|
3546
|
+
return;
|
|
3547
|
+
}
|
|
3548
|
+
if (sub === "rm" || sub === "remove" || sub === "delete") {
|
|
3549
|
+
const flags = parseLocalFlags(args);
|
|
3550
|
+
if ((flags.booleans.has("--permanent") || flags.booleans.has("--hard-delete")) && !ctx.yes && !flags.booleans.has("--yes")) {
|
|
3551
|
+
throw new CliError("Permanent Library deletion cannot be undone. Re-run with --yes to confirm.");
|
|
3552
|
+
}
|
|
3553
|
+
const path = requireCommandValue(flags.positionals[0], "Usage: uru library rm <path> [--permanent] [--yes]");
|
|
3554
|
+
await emitTool(ctx, "library.rm", {
|
|
3555
|
+
path,
|
|
3556
|
+
permanent: flags.booleans.has("--permanent") || flags.booleans.has("--hard-delete")
|
|
3557
|
+
});
|
|
3558
|
+
return;
|
|
3559
|
+
}
|
|
3560
|
+
if (sub === "restore") {
|
|
3561
|
+
await emitTool(ctx, "library.restore", {
|
|
3562
|
+
path: requireCommandValue(args[0], "Usage: uru library restore <path>"),
|
|
3563
|
+
include_trashed: true
|
|
3564
|
+
});
|
|
3565
|
+
return;
|
|
3566
|
+
}
|
|
3567
|
+
if (sub === "empty-trash") {
|
|
3568
|
+
const flags = parseLocalFlags(args);
|
|
3569
|
+
if (!ctx.yes && !flags.booleans.has("--yes")) {
|
|
3570
|
+
throw new CliError("Emptying Library trash permanently deletes all trashed items. Re-run with --yes to confirm.");
|
|
3571
|
+
}
|
|
3572
|
+
await emitTool(ctx, "library.empty_trash", {});
|
|
3573
|
+
return;
|
|
3574
|
+
}
|
|
3575
|
+
throw new CliError("Usage: uru library ls|search|mkdir|read|write|patch|cp|mv|rm|restore|empty-trash");
|
|
3576
|
+
}
|
|
3577
|
+
async function libraryTextFromFlags(ctx, flags, verb) {
|
|
3578
|
+
const content = flags.values["--content"];
|
|
3579
|
+
const file = flags.values["--file"];
|
|
3580
|
+
const fromStdin = flags.booleans.has("--stdin");
|
|
3581
|
+
const sources = [content !== undefined, file !== undefined, fromStdin].filter(Boolean).length;
|
|
3582
|
+
if (sources !== 1) {
|
|
3583
|
+
throw new CliError(`Library ${verb} requires exactly one of --file <path>, --content <text>, or --stdin.`);
|
|
3584
|
+
}
|
|
3585
|
+
if (content !== undefined) {
|
|
3586
|
+
return content;
|
|
3587
|
+
}
|
|
3588
|
+
if (file !== undefined) {
|
|
3589
|
+
return await readFile4(resolve2(ctx.cwd, file), "utf8");
|
|
3590
|
+
}
|
|
3591
|
+
return await readStdinText(ctx.stdin ?? process.stdin);
|
|
3592
|
+
}
|
|
3593
|
+
async function readStdinText(stdin) {
|
|
3594
|
+
const chunks = [];
|
|
3595
|
+
for await (const chunk of stdin) {
|
|
3596
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
3597
|
+
}
|
|
3598
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
3599
|
+
}
|
|
3600
|
+
function extractLibraryTextContent(value) {
|
|
3601
|
+
if (!isJsonObject2(value)) {
|
|
3602
|
+
return;
|
|
3603
|
+
}
|
|
3604
|
+
const content = value["content"];
|
|
3605
|
+
if (typeof content === "string") {
|
|
3606
|
+
return content;
|
|
3607
|
+
}
|
|
3608
|
+
const text = value["text"];
|
|
3609
|
+
return typeof text === "string" ? text : undefined;
|
|
3418
3610
|
}
|
|
3419
3611
|
async function dispatchDatasets(ctx, sub, args) {
|
|
3420
3612
|
if (sub !== "query") {
|
|
@@ -3623,11 +3815,12 @@ function levenshtein(left, right) {
|
|
|
3623
3815
|
}
|
|
3624
3816
|
|
|
3625
3817
|
// src/gem-lifecycle-commands.ts
|
|
3626
|
-
import { mkdir as mkdir4, readFile as
|
|
3818
|
+
import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile4 } from "node:fs/promises";
|
|
3627
3819
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
3628
|
-
import { join as join3, resolve as
|
|
3820
|
+
import { join as join3, resolve as resolve3 } from "node:path";
|
|
3629
3821
|
async function dispatchGems(ctx, sub, args, linkedProject) {
|
|
3630
|
-
const
|
|
3822
|
+
const flags = parseLocalFlags(args);
|
|
3823
|
+
const gemPath = () => gemPathFromArgs(flags, linkedProject);
|
|
3631
3824
|
if (sub === "inspect") {
|
|
3632
3825
|
await emitGemTool(ctx, "gem.inspect", { path: gemPath() });
|
|
3633
3826
|
return;
|
|
@@ -3678,7 +3871,7 @@ async function gemInit(ctx, args) {
|
|
|
3678
3871
|
params["content"] = flags.values["--content"];
|
|
3679
3872
|
}
|
|
3680
3873
|
if (flags.values["--file"] !== undefined) {
|
|
3681
|
-
params["content"] = await
|
|
3874
|
+
params["content"] = await readFile5(flags.values["--file"], "utf8");
|
|
3682
3875
|
}
|
|
3683
3876
|
await emitGemTool(ctx, "gem.init", params);
|
|
3684
3877
|
}
|
|
@@ -3703,6 +3896,12 @@ async function gemDeploy(ctx, args, linkedProject) {
|
|
|
3703
3896
|
}
|
|
3704
3897
|
if (flags.booleans.has("--dry-run")) {
|
|
3705
3898
|
const buildArgs = [...flags.positionals, "--dry-run"];
|
|
3899
|
+
for (const flag of ["--path", "--gem"]) {
|
|
3900
|
+
const value = flags.values[flag];
|
|
3901
|
+
if (value !== undefined) {
|
|
3902
|
+
buildArgs.push(flag, value);
|
|
3903
|
+
}
|
|
3904
|
+
}
|
|
3706
3905
|
if (flags.booleans.has("--force-build")) {
|
|
3707
3906
|
buildArgs.push("--force");
|
|
3708
3907
|
}
|
|
@@ -3713,7 +3912,7 @@ async function gemDeploy(ctx, args, linkedProject) {
|
|
|
3713
3912
|
await gemBuild(ctx, buildArgs, linkedProject);
|
|
3714
3913
|
return;
|
|
3715
3914
|
}
|
|
3716
|
-
const gemPath = gemPathFromArgs(flags
|
|
3915
|
+
const gemPath = gemPathFromArgs(flags, linkedProject);
|
|
3717
3916
|
const publish = !flags.booleans.has("--no-publish") && !flags.booleans.has("--skip-promote");
|
|
3718
3917
|
const params = {
|
|
3719
3918
|
path: gemPath,
|
|
@@ -3742,7 +3941,7 @@ async function gemDeploy(ctx, args, linkedProject) {
|
|
|
3742
3941
|
async function gemBuild(ctx, args, linkedProject) {
|
|
3743
3942
|
const flags = parseLocalFlags(args);
|
|
3744
3943
|
assertAllowedLocalFlags(flags, ["--dry-run", "--force", "--gem", "--outdir", "--path", "--source-version-id"], "uru build");
|
|
3745
|
-
const gemPath = gemPathFromArgs(flags
|
|
3944
|
+
const gemPath = gemPathFromArgs(flags, linkedProject);
|
|
3746
3945
|
const params = {
|
|
3747
3946
|
path: gemPath
|
|
3748
3947
|
};
|
|
@@ -3791,9 +3990,9 @@ async function gemChecks(ctx, args, linkedProject) {
|
|
|
3791
3990
|
}
|
|
3792
3991
|
assertAllowedLocalFlags(flags, ["--deployment-id", "--gem", "--path"], "uru gems checks");
|
|
3793
3992
|
const explicitDeploymentId = flags.values["--deployment-id"];
|
|
3794
|
-
const
|
|
3993
|
+
const gemPathFlags = explicitDeploymentId === undefined ? shiftPositionals(flags, 1) : flags;
|
|
3795
3994
|
const params = {
|
|
3796
|
-
path: gemPathFromArgs(
|
|
3995
|
+
path: gemPathFromArgs(gemPathFlags, linkedProject)
|
|
3797
3996
|
};
|
|
3798
3997
|
if (explicitDeploymentId !== undefined) {
|
|
3799
3998
|
params["deployment_id"] = explicitDeploymentId;
|
|
@@ -3806,7 +4005,7 @@ async function gemRunCheck(ctx, flags, linkedProject) {
|
|
|
3806
4005
|
assertAllowedLocalFlags(flags, ["--blocking", "--deep", "--deployment-id", "--gem", "--path"], "uru gems checks run");
|
|
3807
4006
|
const kind = requireCommandValue(flags.positionals[1], "Usage: uru gems checks run browser|security [library/name.gem] [--deployment-id <id>] [--blocking]");
|
|
3808
4007
|
const params = {
|
|
3809
|
-
path: gemPathFromArgs(flags
|
|
4008
|
+
path: gemPathFromArgs(shiftPositionals(flags, 2), linkedProject)
|
|
3810
4009
|
};
|
|
3811
4010
|
copyStringFlag(flags, params, "--deployment-id", "deployment_id");
|
|
3812
4011
|
if (flags.booleans.has("--blocking")) {
|
|
@@ -3842,7 +4041,7 @@ async function gemLogs(ctx, args, linkedProject) {
|
|
|
3842
4041
|
"--request-id"
|
|
3843
4042
|
], "uru logs");
|
|
3844
4043
|
const params = {
|
|
3845
|
-
path: gemPathFromArgs(flags
|
|
4044
|
+
path: gemPathFromArgs(flags, linkedProject),
|
|
3846
4045
|
limit: integerFlag(flags, "--limit", 100)
|
|
3847
4046
|
};
|
|
3848
4047
|
copyStringFlag(flags, params, "--level", "level");
|
|
@@ -3990,8 +4189,9 @@ function boundedIntegerFlag(flags, name, fallback, maximum) {
|
|
|
3990
4189
|
return Math.min(value, maximum);
|
|
3991
4190
|
}
|
|
3992
4191
|
async function gemOpen(ctx, args, linkedProject) {
|
|
4192
|
+
const flags = parseLocalFlags(args);
|
|
3993
4193
|
const result = await executeOperation(ctx, "gem.links_list", {
|
|
3994
|
-
path: gemPathFromArgs(
|
|
4194
|
+
path: gemPathFromArgs(flags, linkedProject)
|
|
3995
4195
|
});
|
|
3996
4196
|
const url = firstUrl(unwrapToolResult(result));
|
|
3997
4197
|
if (url === undefined) {
|
|
@@ -4022,7 +4222,7 @@ async function gemWrite(ctx, args) {
|
|
|
4022
4222
|
const flags = parseLocalFlags(args);
|
|
4023
4223
|
const gemPath = requireCommandValue(flags.positionals[0], "Usage: uru gems write <gem-path> <file> --content <text>|--file <local-file>");
|
|
4024
4224
|
const filePath = requireCommandValue(flags.positionals[1], "Usage: uru gems write <gem-path> <file> --content <text>|--file <local-file>");
|
|
4025
|
-
const content = flags.values["--content"] ?? (flags.values["--file"] === undefined ? undefined : await
|
|
4225
|
+
const content = flags.values["--content"] ?? (flags.values["--file"] === undefined ? undefined : await readFile5(flags.values["--file"], "utf8"));
|
|
4026
4226
|
if (content === undefined) {
|
|
4027
4227
|
throw new CliError("Gem write requires --content or --file.");
|
|
4028
4228
|
}
|
|
@@ -4049,7 +4249,7 @@ async function dispatchGemVersions(ctx, args, linkedProject) {
|
|
|
4049
4249
|
assertAllowedLocalFlags(flags, ["--gem", "--link-id", "--no-wait", "--path", "--pct", "--skip-promote"], "uru gems versions deploy");
|
|
4050
4250
|
const version = requireCommandValue(flags.positionals[0], "Usage: uru gems versions deploy <version-id> [library/name.gem] [--pct 100] [--skip-promote]");
|
|
4051
4251
|
const [versionId, trafficPercent] = version.split("@", 2);
|
|
4052
|
-
const gemPath = gemPathFromArgs(flags
|
|
4252
|
+
const gemPath = gemPathFromArgs(shiftPositionals(flags, 1), linkedProject);
|
|
4053
4253
|
const pct = flags.values["--pct"] ?? trafficPercent;
|
|
4054
4254
|
const trafficPercentValue = pct === undefined || pct === "" ? 100 : parseTrafficPercent(pct);
|
|
4055
4255
|
const publish = !flags.booleans.has("--skip-promote");
|
|
@@ -4086,7 +4286,7 @@ async function gemVersionUpload(ctx, args, linkedProject) {
|
|
|
4086
4286
|
"--tag",
|
|
4087
4287
|
"--tags"
|
|
4088
4288
|
], "uru gems versions upload");
|
|
4089
|
-
const gemPath = gemPathFromArgs(flags
|
|
4289
|
+
const gemPath = gemPathFromArgs(flags, linkedProject);
|
|
4090
4290
|
const params = {
|
|
4091
4291
|
path: gemPath
|
|
4092
4292
|
};
|
|
@@ -4122,7 +4322,7 @@ async function deployVersionToPublishedLink(ctx, flags, linkedProject, options)
|
|
|
4122
4322
|
if (deploymentId === undefined && versionId === undefined) {
|
|
4123
4323
|
throw new CliError(options.usage);
|
|
4124
4324
|
}
|
|
4125
|
-
const gemPath = gemPathFromArgs(versionId === undefined ? flags
|
|
4325
|
+
const gemPath = gemPathFromArgs(versionId === undefined ? flags : shiftPositionals(flags, 1), linkedProject);
|
|
4126
4326
|
const trafficPercent = parseTrafficPercent(flags.values["--pct"] ?? "100");
|
|
4127
4327
|
if (trafficPercent !== 100) {
|
|
4128
4328
|
throw new CliError(`${options.operation} does not support partial traffic shifts yet. Deploy the version with --skip-promote for validation, or ${options.operation} with --pct 100.`);
|
|
@@ -4179,7 +4379,7 @@ function localFlagNames(flags) {
|
|
|
4179
4379
|
async function writeBuildManifest(ctx, outdir, buildResult) {
|
|
4180
4380
|
const manifest = gemBuildManifestFromBuildResult(buildResult);
|
|
4181
4381
|
const providerDecision = gemProviderDecisionFromBuildResult(buildResult, manifest);
|
|
4182
|
-
const absoluteOutdir =
|
|
4382
|
+
const absoluteOutdir = resolve3(ctx.cwd, outdir);
|
|
4183
4383
|
await mkdir4(absoluteOutdir, { recursive: true });
|
|
4184
4384
|
await writeFile4(join3(absoluteOutdir, "gem-build-manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
4185
4385
|
`, { mode: 384 });
|
|
@@ -4378,10 +4578,10 @@ function trimLeadingSlashes(value) {
|
|
|
4378
4578
|
|
|
4379
4579
|
// src/gem-source-commands.ts
|
|
4380
4580
|
import { mkdir as mkdir5, rm as rm3, writeFile as writeFile5 } from "node:fs/promises";
|
|
4381
|
-
import { dirname as dirname4, join as join4, resolve as
|
|
4581
|
+
import { dirname as dirname4, join as join4, resolve as resolve4 } from "node:path";
|
|
4382
4582
|
async function gemSourcePull(ctx, args, linkedProject) {
|
|
4383
4583
|
const flags = parseLocalFlags(args);
|
|
4384
|
-
const gemPath = gemPathFromArgs(flags
|
|
4584
|
+
const gemPath = gemPathFromArgs(flags, linkedProject);
|
|
4385
4585
|
const force = flags.booleans.has("--force");
|
|
4386
4586
|
const previousLock = await readSourceLock(ctx.cwd);
|
|
4387
4587
|
const localFilesBeforePull = await collectLocalSourceFiles(ctx.cwd);
|
|
@@ -4401,15 +4601,19 @@ async function gemSourcePull(ctx, args, linkedProject) {
|
|
|
4401
4601
|
}
|
|
4402
4602
|
let sourceVersionId = findSourceVersionId(tree);
|
|
4403
4603
|
const files = {};
|
|
4604
|
+
const pulledFiles = [];
|
|
4404
4605
|
for (const filePath of filePaths) {
|
|
4405
4606
|
const readResult = unwrapToolResult(await executeOperation(ctx, "gem.fs_read", {
|
|
4406
4607
|
path: gemFilePath(gemPath, filePath)
|
|
4407
4608
|
}));
|
|
4408
4609
|
sourceVersionId = sourceVersionId ?? findSourceVersionId(readResult);
|
|
4409
4610
|
const content = extractRemoteFileContent(readResult, filePath);
|
|
4410
|
-
|
|
4611
|
+
pulledFiles.push({ path: filePath, content });
|
|
4411
4612
|
files[filePath] = sha256Hex(content);
|
|
4412
4613
|
}
|
|
4614
|
+
for (const file of pulledFiles) {
|
|
4615
|
+
await writePulledSourceFile(ctx.cwd, file.path, file.content);
|
|
4616
|
+
}
|
|
4413
4617
|
const pruned = await pruneRemoteDeletedSourceFiles(ctx.cwd, previousLock, localFilesBeforePull, new Set(filePaths));
|
|
4414
4618
|
const lock = {
|
|
4415
4619
|
...sourceVersionId === undefined ? {} : { sourceVersionId },
|
|
@@ -4428,11 +4632,11 @@ async function gemSourcePull(ctx, args, linkedProject) {
|
|
|
4428
4632
|
}
|
|
4429
4633
|
async function gemSourceStatus(ctx, args, linkedProject) {
|
|
4430
4634
|
const flags = parseLocalFlags(args);
|
|
4431
|
-
const gemPath = gemPathFromArgs(flags
|
|
4635
|
+
const gemPath = gemPathFromArgs(flags, linkedProject);
|
|
4432
4636
|
const lock = await readSourceLock(ctx.cwd);
|
|
4433
4637
|
const localFiles = await collectLocalSourceFiles(ctx.cwd);
|
|
4434
4638
|
const localStatus = compareSourceLock(lock, localFiles);
|
|
4435
|
-
const remote = await
|
|
4639
|
+
const remote = await remoteSourceOperation(ctx, "gem.fs_status", {
|
|
4436
4640
|
path: gemPath,
|
|
4437
4641
|
...lock.sourceVersionId === undefined ? {} : { base: lock.sourceVersionId }
|
|
4438
4642
|
});
|
|
@@ -4447,12 +4651,12 @@ async function gemSourceStatus(ctx, args, linkedProject) {
|
|
|
4447
4651
|
}
|
|
4448
4652
|
async function gemSourceDiff(ctx, args, linkedProject) {
|
|
4449
4653
|
const flags = parseLocalFlags(args);
|
|
4450
|
-
const gemPath = gemPathFromArgs(flags
|
|
4654
|
+
const gemPath = gemPathFromArgs(flags, linkedProject);
|
|
4451
4655
|
const lock = await readSourceLock(ctx.cwd);
|
|
4452
4656
|
const localFiles = await collectLocalSourceFiles(ctx.cwd);
|
|
4453
4657
|
const localStatus = compareSourceLock(lock, localFiles);
|
|
4454
4658
|
const mode = flags.values["--mode"] ?? "name_status";
|
|
4455
|
-
const remote = await
|
|
4659
|
+
const remote = await remoteSourceOperation(ctx, "gem.fs_diff", {
|
|
4456
4660
|
path: gemPath,
|
|
4457
4661
|
mode,
|
|
4458
4662
|
...lock.sourceVersionId === undefined ? {} : { base: lock.sourceVersionId }
|
|
@@ -4468,7 +4672,7 @@ async function gemSourceDiff(ctx, args, linkedProject) {
|
|
|
4468
4672
|
}
|
|
4469
4673
|
async function gemSourcePush(ctx, args, linkedProject) {
|
|
4470
4674
|
const flags = parseLocalFlags(args);
|
|
4471
|
-
const gemPath = gemPathFromArgs(flags
|
|
4675
|
+
const gemPath = gemPathFromArgs(flags, linkedProject);
|
|
4472
4676
|
const lock = await readSourceLock(ctx.cwd);
|
|
4473
4677
|
const force = flags.booleans.has("--force");
|
|
4474
4678
|
const localFiles = await collectLocalSourceFiles(ctx.cwd);
|
|
@@ -4495,6 +4699,9 @@ async function gemSourcePush(ctx, args, linkedProject) {
|
|
|
4495
4699
|
}));
|
|
4496
4700
|
sourceCas = nextSourceMutationCas(sourceCas, syncResult);
|
|
4497
4701
|
} catch (error) {
|
|
4702
|
+
if (error instanceof CliError && error.exitCode === ExitCode.Conflict) {
|
|
4703
|
+
throw error;
|
|
4704
|
+
}
|
|
4498
4705
|
if (isConflictLikeError(error)) {
|
|
4499
4706
|
throw new CliError(error instanceof Error ? error.message : "Gem source conflict", { code: ExitCode.Conflict, cause: error });
|
|
4500
4707
|
}
|
|
@@ -4541,7 +4748,7 @@ async function pruneRemoteDeletedSourceFiles(cwd, previousLock, localFilesBefore
|
|
|
4541
4748
|
preserved.push(projectPath2);
|
|
4542
4749
|
continue;
|
|
4543
4750
|
}
|
|
4544
|
-
await rm3(join4(
|
|
4751
|
+
await rm3(join4(resolve4(cwd), ...safePath.split("/")), { force: true });
|
|
4545
4752
|
deleted.push(projectPath2);
|
|
4546
4753
|
}
|
|
4547
4754
|
return { deleted, preserved };
|
|
@@ -4660,18 +4867,11 @@ function dirtyPullErrorMessage(status) {
|
|
|
4660
4867
|
Run \`uru status\` to inspect them, save or push your changes, or re-run \`uru pull --force\` to replace local files with server source.
|
|
4661
4868
|
${preview}${moreLine}`;
|
|
4662
4869
|
}
|
|
4663
|
-
async function
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
};
|
|
4669
|
-
} catch (error) {
|
|
4670
|
-
return {
|
|
4671
|
-
available: false,
|
|
4672
|
-
error: error instanceof Error ? error.message : "Remote operation failed"
|
|
4673
|
-
};
|
|
4674
|
-
}
|
|
4870
|
+
async function remoteSourceOperation(ctx, operationId, params) {
|
|
4871
|
+
return {
|
|
4872
|
+
available: true,
|
|
4873
|
+
result: unwrapToolResult(await executeOperation(ctx, operationId, params))
|
|
4874
|
+
};
|
|
4675
4875
|
}
|
|
4676
4876
|
function formatRemoteSummary(remote) {
|
|
4677
4877
|
if (!remote.available) {
|
|
@@ -4762,7 +4962,7 @@ async function writePulledSourceFile(cwd, projectPath2, content) {
|
|
|
4762
4962
|
if (safePath === null) {
|
|
4763
4963
|
throw new CliError(`Refusing to write unsafe Gem source path: ${projectPath2}`);
|
|
4764
4964
|
}
|
|
4765
|
-
const localPath = join4(
|
|
4965
|
+
const localPath = join4(resolve4(cwd), ...safePath.split("/"));
|
|
4766
4966
|
await mkdir5(dirname4(localPath), { recursive: true });
|
|
4767
4967
|
await writeFile5(localPath, content);
|
|
4768
4968
|
}
|
|
@@ -4817,8 +5017,9 @@ function isConflictLikeError(error) {
|
|
|
4817
5017
|
|
|
4818
5018
|
// src/token-env-secret-commands.ts
|
|
4819
5019
|
import { spawn as spawn2 } from "node:child_process";
|
|
4820
|
-
import {
|
|
4821
|
-
import {
|
|
5020
|
+
import { StringDecoder } from "node:string_decoder";
|
|
5021
|
+
import { chmod, mkdir as mkdir6, readFile as readFile6, writeFile as writeFile6 } from "node:fs/promises";
|
|
5022
|
+
import { dirname as dirname5, resolve as resolve5 } from "node:path";
|
|
4822
5023
|
async function dispatchTokens(ctx, sub, args, linkedProject) {
|
|
4823
5024
|
if (sub === "create") {
|
|
4824
5025
|
const flags = parseLocalFlags(args);
|
|
@@ -4932,7 +5133,7 @@ async function dispatchEnv(ctx, sub, args, linkedProject) {
|
|
|
4932
5133
|
})));
|
|
4933
5134
|
const secrets = extractSecretRows(result);
|
|
4934
5135
|
const content = formatEnvTemplate(secrets);
|
|
4935
|
-
const absolutePath =
|
|
5136
|
+
const absolutePath = resolve5(ctx.cwd, outputPath);
|
|
4936
5137
|
await mkdir6(dirname5(absolutePath), { recursive: true });
|
|
4937
5138
|
await writeFile6(absolutePath, content, { mode: 384 });
|
|
4938
5139
|
await chmod(absolutePath, 384);
|
|
@@ -4949,8 +5150,8 @@ async function dispatchEnv(ctx, sub, args, linkedProject) {
|
|
|
4949
5150
|
}
|
|
4950
5151
|
if (sub === "run") {
|
|
4951
5152
|
const parsed = parseEnvRunArgs(args);
|
|
4952
|
-
const envFilePath =
|
|
4953
|
-
const envVars = parseEnvFile(await
|
|
5153
|
+
const envFilePath = resolve5(ctx.cwd, parsed.envFile);
|
|
5154
|
+
const envVars = parseEnvFile(await readFile6(envFilePath, "utf8"));
|
|
4954
5155
|
await runEnvCommand(ctx, parsed.command, envVars);
|
|
4955
5156
|
return;
|
|
4956
5157
|
}
|
|
@@ -4970,10 +5171,10 @@ async function secretValueFromFlags(ctx, flags) {
|
|
|
4970
5171
|
}
|
|
4971
5172
|
const valueFile = flags.values["--value-file"];
|
|
4972
5173
|
if (valueFile !== undefined) {
|
|
4973
|
-
return (await
|
|
5174
|
+
return (await readFile6(resolve5(ctx.cwd, valueFile), "utf8")).trimEnd();
|
|
4974
5175
|
}
|
|
4975
5176
|
if (flags.booleans.has("--value-stdin")) {
|
|
4976
|
-
return await
|
|
5177
|
+
return await readStdinText2(ctx.stdin ?? process.stdin);
|
|
4977
5178
|
}
|
|
4978
5179
|
throw new CliError("Secret put requires --value-file <path> or --value-stdin. Values are never printed by the CLI.");
|
|
4979
5180
|
}
|
|
@@ -5068,48 +5269,69 @@ function unquoteEnvValue(value) {
|
|
|
5068
5269
|
}
|
|
5069
5270
|
async function runEnvCommand(ctx, command, envVars) {
|
|
5070
5271
|
const executable = requireCommandValue(command[0], "env run command is required");
|
|
5071
|
-
const
|
|
5272
|
+
const redactions = Object.values(envVars).filter((value) => value.length > 0);
|
|
5273
|
+
const code = await spawnStreaming(executable, command.slice(1), {
|
|
5072
5274
|
cwd: ctx.cwd,
|
|
5073
|
-
env: { ...process.env, ...envVars }
|
|
5275
|
+
env: { ...process.env, ...envVars },
|
|
5276
|
+
redactions,
|
|
5277
|
+
stdout: ctx.io.stdout,
|
|
5278
|
+
stderr: ctx.io.stderr
|
|
5074
5279
|
});
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
const stderr = redactValues2(result.stderr, redactions);
|
|
5078
|
-
if (stdout.length > 0) {
|
|
5079
|
-
ctx.io.stdout.write(stdout);
|
|
5080
|
-
}
|
|
5081
|
-
if (stderr.length > 0) {
|
|
5082
|
-
ctx.io.stderr.write(stderr);
|
|
5083
|
-
}
|
|
5084
|
-
if (result.code !== 0) {
|
|
5085
|
-
throw new CliError(`env run failed with exit code ${result.code}`);
|
|
5280
|
+
if (code !== 0) {
|
|
5281
|
+
throw new CliError(`env run failed with exit code ${code}`);
|
|
5086
5282
|
}
|
|
5087
5283
|
}
|
|
5088
|
-
async function
|
|
5284
|
+
async function spawnStreaming(command, args, options) {
|
|
5089
5285
|
return new Promise((resolvePromise, reject) => {
|
|
5090
5286
|
const child = spawn2(command, [...args], {
|
|
5091
5287
|
cwd: options.cwd,
|
|
5092
5288
|
env: options.env,
|
|
5093
|
-
stdio: ["
|
|
5289
|
+
stdio: ["inherit", "pipe", "pipe"]
|
|
5094
5290
|
});
|
|
5095
|
-
const stdout =
|
|
5096
|
-
const stderr =
|
|
5291
|
+
const stdout = redactingLineWriter(options.stdout, options.redactions);
|
|
5292
|
+
const stderr = redactingLineWriter(options.stderr, options.redactions);
|
|
5097
5293
|
child.stdout?.on("data", (chunk) => {
|
|
5098
|
-
stdout.
|
|
5294
|
+
stdout.write(chunk);
|
|
5099
5295
|
});
|
|
5100
5296
|
child.stderr?.on("data", (chunk) => {
|
|
5101
|
-
stderr.
|
|
5297
|
+
stderr.write(chunk);
|
|
5102
5298
|
});
|
|
5103
5299
|
child.on("error", reject);
|
|
5104
5300
|
child.on("close", (code) => {
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
stderr: stderr.join("")
|
|
5109
|
-
});
|
|
5301
|
+
stdout.end();
|
|
5302
|
+
stderr.end();
|
|
5303
|
+
resolvePromise(code ?? 1);
|
|
5110
5304
|
});
|
|
5111
5305
|
});
|
|
5112
5306
|
}
|
|
5307
|
+
function redactingLineWriter(destination, redactions) {
|
|
5308
|
+
const decoder = new StringDecoder("utf8");
|
|
5309
|
+
let buffered = "";
|
|
5310
|
+
const flushCompleteLines = () => {
|
|
5311
|
+
let newline = buffered.indexOf(`
|
|
5312
|
+
`);
|
|
5313
|
+
while (newline !== -1) {
|
|
5314
|
+
destination.write(redactValues2(buffered.slice(0, newline + 1), redactions));
|
|
5315
|
+
buffered = buffered.slice(newline + 1);
|
|
5316
|
+
newline = buffered.indexOf(`
|
|
5317
|
+
`);
|
|
5318
|
+
}
|
|
5319
|
+
};
|
|
5320
|
+
return {
|
|
5321
|
+
write: (chunk) => {
|
|
5322
|
+
buffered += decoder.write(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
5323
|
+
flushCompleteLines();
|
|
5324
|
+
},
|
|
5325
|
+
end: () => {
|
|
5326
|
+
buffered += decoder.end();
|
|
5327
|
+
flushCompleteLines();
|
|
5328
|
+
if (buffered.length > 0) {
|
|
5329
|
+
destination.write(redactValues2(buffered, redactions));
|
|
5330
|
+
buffered = "";
|
|
5331
|
+
}
|
|
5332
|
+
}
|
|
5333
|
+
};
|
|
5334
|
+
}
|
|
5113
5335
|
function redactValues2(content, values) {
|
|
5114
5336
|
let redacted = content;
|
|
5115
5337
|
const ordered = [...new Set(values)].filter((value) => value.length > 0).sort((left, right) => right.length - left.length);
|
|
@@ -5118,7 +5340,7 @@ function redactValues2(content, values) {
|
|
|
5118
5340
|
}
|
|
5119
5341
|
return redacted;
|
|
5120
5342
|
}
|
|
5121
|
-
async function
|
|
5343
|
+
async function readStdinText2(stdin) {
|
|
5122
5344
|
const chunks = [];
|
|
5123
5345
|
for await (const chunk of stdin) {
|
|
5124
5346
|
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
@@ -5257,7 +5479,7 @@ async function runCli(options) {
|
|
|
5257
5479
|
const explicitGemRef = parsed.flags.gem ?? envGemId;
|
|
5258
5480
|
const apiUrl = parsed.flags.apiUrl ?? envApiUrl ?? config.apiUrl ?? DEFAULT_API_URL;
|
|
5259
5481
|
const token = flagToken ?? envToken ?? await resolveStoredToken(credentialStore, apiUrl, config);
|
|
5260
|
-
const cwd = parsed.flags.cwd ??
|
|
5482
|
+
const cwd = parsed.flags.cwd ?? process.cwd();
|
|
5261
5483
|
const linkedProjectFromDisk = await readLinkedProject(cwd);
|
|
5262
5484
|
const linkedProject = explicitGemRef === undefined ? linkedProjectFromDisk : linkedProjectWithExplicitGemRef(linkedProjectFromDisk, explicitGemRef);
|
|
5263
5485
|
const workspace = parsed.flags.workspace ?? envWorkspace ?? linkedProject.workspaceId ?? config.workspace;
|
|
@@ -5594,14 +5816,14 @@ function writeSystemClipboard(text) {
|
|
|
5594
5816
|
const platform = process.platform;
|
|
5595
5817
|
const command = platform === "darwin" ? "pbcopy" : platform === "win32" ? "clip" : "xclip";
|
|
5596
5818
|
const args = platform === "linux" ? ["-selection", "clipboard"] : [];
|
|
5597
|
-
return new Promise((
|
|
5819
|
+
return new Promise((resolve6, reject) => {
|
|
5598
5820
|
const child = spawn3(command, args, { stdio: ["pipe", "ignore", "ignore"] });
|
|
5599
5821
|
child.on("error", (error) => {
|
|
5600
5822
|
reject(new CliError("Could not copy login URL to clipboard. Re-run without --clipboard to print the URL.", { cause: error }));
|
|
5601
5823
|
});
|
|
5602
5824
|
child.on("close", (code) => {
|
|
5603
5825
|
if (code === 0) {
|
|
5604
|
-
|
|
5826
|
+
resolve6();
|
|
5605
5827
|
return;
|
|
5606
5828
|
}
|
|
5607
5829
|
reject(new CliError(`Could not copy login URL to clipboard (${command} exited ${code ?? "unknown"}). Re-run without --clipboard to print the URL.`));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uru-intelligence/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.56",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Uru full-platform command line interface",
|
|
6
6
|
"type": "module",
|
|
@@ -31,13 +31,13 @@
|
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@uru/platform-operations": "workspace:*",
|
|
34
|
-
"@types/node": "24.13.
|
|
34
|
+
"@types/node": "24.13.3",
|
|
35
35
|
"bun-types": "1.3.13",
|
|
36
|
-
"typescript": "7.0.
|
|
36
|
+
"typescript": "7.0.2"
|
|
37
37
|
},
|
|
38
38
|
"packageManager": "bun@1.3.13",
|
|
39
39
|
"engines": {
|
|
40
|
-
"node": "24
|
|
40
|
+
"node": ">=24"
|
|
41
41
|
},
|
|
42
42
|
"files": [
|
|
43
43
|
"dist",
|