@general-input/cli 0.2.0 → 0.3.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/dist/cli.js CHANGED
@@ -384,7 +384,7 @@ import chalk4 from "chalk";
384
384
  // package.json
385
385
  var package_default = {
386
386
  name: "@general-input/cli",
387
- version: "0.2.0",
387
+ version: "0.3.0",
388
388
  type: "module",
389
389
  description: "The agent-facing CLI for General Input. Authenticate, manage workflows, run bash with operator credentials injected by the cloud.",
390
390
  license: "SEE LICENSE IN LICENSE",
@@ -1072,7 +1072,7 @@ import {
1072
1072
  existsSync,
1073
1073
  statSync
1074
1074
  } from "fs";
1075
- import { join, dirname, relative, sep } from "path";
1075
+ import { join, relative, sep } from "path";
1076
1076
  var RESOURCE_MARKER_FILE = ".geni-resource.json";
1077
1077
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".turbo"]);
1078
1078
  function readMarker(dir) {
@@ -1107,14 +1107,14 @@ function resolveResourceId(args) {
1107
1107
  );
1108
1108
  exit(ExitCode.InvalidArgs);
1109
1109
  }
1110
- function readLocalFiles(dir) {
1110
+ function listResourceRelPaths(dir) {
1111
1111
  if (!existsSync(dir)) {
1112
1112
  printError(`Directory not found: ${dir}`);
1113
1113
  exit(ExitCode.InvalidArgs);
1114
1114
  }
1115
- const files = [];
1116
- walk(dir, dir, files);
1117
- return files;
1115
+ const out = [];
1116
+ walk(dir, dir, out);
1117
+ return out;
1118
1118
  }
1119
1119
  function walk(root, current, out) {
1120
1120
  for (const entry of readdirSync(current)) {
@@ -1127,27 +1127,9 @@ function walk(root, current, out) {
1127
1127
  continue;
1128
1128
  }
1129
1129
  if (!stat.isFile()) continue;
1130
- const rel = relative(root, full).split(sep).join("/");
1131
- out.push(bufferToCliFile(rel, readFileSync(full)));
1132
- }
1133
- }
1134
- function writeLocalFiles(dir, files) {
1135
- for (const file of files) {
1136
- const target = join(dir, file.path);
1137
- mkdirSync(dirname(target), { recursive: true });
1138
- writeFileSync(target, cliFileBytes(file));
1130
+ out.push(relative(root, full).split(sep).join("/"));
1139
1131
  }
1140
1132
  }
1141
- function bufferToCliFile(path, buf) {
1142
- const asUtf8 = buf.toString("utf-8");
1143
- if (Buffer.from(asUtf8, "utf-8").equals(buf)) {
1144
- return { path, content: asUtf8, encoding: "utf8" };
1145
- }
1146
- return { path, content: buf.toString("base64"), encoding: "base64" };
1147
- }
1148
- function cliFileBytes(file) {
1149
- return file.encoding === "base64" ? Buffer.from(file.content, "base64") : file.content;
1150
- }
1151
1133
  var MIME_BY_EXT = {
1152
1134
  json: "application/json",
1153
1135
  csv: "text/csv",
@@ -1176,6 +1158,32 @@ function dirHasResourceFiles(dir) {
1176
1158
  );
1177
1159
  }
1178
1160
 
1161
+ // src/lib/bundleArchive.ts
1162
+ import { mkdirSync as mkdirSync2 } from "fs";
1163
+ import { Readable } from "stream";
1164
+ import { create, extract } from "tar";
1165
+ async function packResourceDir(dir) {
1166
+ const files = listResourceRelPaths(dir);
1167
+ const stream = create({ gzip: true, cwd: dir, portable: true }, files);
1168
+ const chunks = [];
1169
+ for await (const chunk of stream) {
1170
+ chunks.push(Buffer.from(chunk));
1171
+ }
1172
+ return Buffer.concat(chunks);
1173
+ }
1174
+ async function extractBundle(dir, buffer) {
1175
+ mkdirSync2(dir, { recursive: true });
1176
+ await new Promise((resolve20, reject) => {
1177
+ const unpack = extract({
1178
+ cwd: dir,
1179
+ filter: (path) => !SKIP_DIRS.has(path.split("/")[0])
1180
+ });
1181
+ unpack.on("close", resolve20);
1182
+ unpack.on("error", reject);
1183
+ Readable.from(buffer).on("error", reject).pipe(unpack);
1184
+ });
1185
+ }
1186
+
1179
1187
  // src/services/WorkflowAuthoringService.ts
1180
1188
  var WorkflowAuthoringService = class {
1181
1189
  constructor(sessionContext) {
@@ -1189,13 +1197,13 @@ var WorkflowAuthoringService = class {
1189
1197
  workflowType: args.workflowType,
1190
1198
  name: args.name
1191
1199
  });
1192
- const filesResponse = await client.workflows.files(detail.id);
1193
- writeLocalFiles(args.dir, filesResponse.files);
1200
+ const { downloadUrl } = await client.workflows.bundleUrl(detail.id);
1201
+ const fileCount = await this.downloadBundleInto(downloadUrl, args.dir);
1194
1202
  writeMarker(args.dir, {
1195
1203
  resourceId: detail.id,
1196
1204
  resourceType: detail.workflowType
1197
1205
  });
1198
- return { detail, fileCount: filesResponse.files.length };
1206
+ return { detail, fileCount };
1199
1207
  }
1200
1208
  async list() {
1201
1209
  const { client } = await this.sessionContext.requireAuthed();
@@ -1212,30 +1220,54 @@ var WorkflowAuthoringService = class {
1212
1220
  /** Download a workflow's live files into `dir` + write its marker. */
1213
1221
  async pull(args) {
1214
1222
  const { client } = await this.sessionContext.requireAuthed();
1215
- const response = await client.workflows.files(args.id);
1216
- writeLocalFiles(args.dir, response.files);
1223
+ const { workflowType, downloadUrl } = await client.workflows.bundleUrl(
1224
+ args.id
1225
+ );
1226
+ const fileCount = await this.downloadBundleInto(downloadUrl, args.dir);
1217
1227
  writeMarker(args.dir, {
1218
1228
  resourceId: args.id,
1219
- resourceType: response.workflowType
1229
+ resourceType: workflowType
1220
1230
  });
1221
- return {
1222
- fileCount: response.files.length,
1223
- workflowType: response.workflowType
1224
- };
1231
+ return { fileCount, workflowType };
1225
1232
  }
1226
1233
  async validate(args) {
1227
1234
  const { client } = await this.sessionContext.requireAuthed();
1228
- const files = readLocalFiles(args.dir);
1229
- return client.workflows.validate(args.id, { files });
1235
+ const bundleKey = await this.uploadBundle(args.id, args.dir);
1236
+ return client.workflows.validate(args.id, { bundleKey });
1230
1237
  }
1231
1238
  async publish(args) {
1232
1239
  const { client } = await this.sessionContext.requireAuthed();
1233
- const files = readLocalFiles(args.dir);
1240
+ const bundleKey = await this.uploadBundle(args.id, args.dir);
1234
1241
  return client.workflows.publish(args.id, {
1235
- files,
1242
+ bundleKey,
1236
1243
  changeSummary: args.changeSummary
1237
1244
  });
1238
1245
  }
1246
+ /** Tar `dir`, PUT it to a presigned URL, return the staged `bundleKey`. */
1247
+ async uploadBundle(id, dir) {
1248
+ const { client } = await this.sessionContext.requireAuthed();
1249
+ const buffer = await packResourceDir(dir);
1250
+ const { uploadUrl, bundleKey, contentType } = await client.workflows.bundleUploadUrl(id);
1251
+ const response = await fetch(uploadUrl, {
1252
+ method: "PUT",
1253
+ headers: { "Content-Type": contentType },
1254
+ body: new Uint8Array(buffer)
1255
+ });
1256
+ if (!response.ok) {
1257
+ throw new Error(`Bundle upload failed (HTTP ${response.status}).`);
1258
+ }
1259
+ return bundleKey;
1260
+ }
1261
+ /** GET a presigned bundle URL, extract it into `dir`, return the file count. */
1262
+ async downloadBundleInto(downloadUrl, dir) {
1263
+ const response = await fetch(downloadUrl);
1264
+ if (!response.ok) {
1265
+ throw new Error(`Bundle download failed (HTTP ${response.status}).`);
1266
+ }
1267
+ const buffer = Buffer.from(await response.arrayBuffer());
1268
+ await extractBundle(dir, buffer);
1269
+ return listResourceRelPaths(dir).length;
1270
+ }
1239
1271
  async getConfig(id) {
1240
1272
  const { client } = await this.sessionContext.requireAuthed();
1241
1273
  return client.workflows.getConfig(id);
@@ -1467,9 +1499,18 @@ var WorkflowsApiClient = class {
1467
1499
  body
1468
1500
  });
1469
1501
  }
1470
- async files(id) {
1502
+ async bundleUrl(id) {
1471
1503
  this.http.requireAuthed();
1472
- return this.http.fetch(`/cli/workflows/${encodeURIComponent(id)}/files`);
1504
+ return this.http.fetch(
1505
+ `/cli/workflows/${encodeURIComponent(id)}/bundle-url`
1506
+ );
1507
+ }
1508
+ async bundleUploadUrl(id) {
1509
+ this.http.requireAuthed();
1510
+ return this.http.fetch(
1511
+ `/cli/workflows/${encodeURIComponent(id)}/bundle-upload-url`,
1512
+ { method: "POST" }
1513
+ );
1473
1514
  }
1474
1515
  async validate(id, body) {
1475
1516
  this.http.requireAuthed();
@@ -1720,7 +1761,7 @@ function isErrnoCode(err, expected) {
1720
1761
  // src/clients/ConfigStore.ts
1721
1762
  import { readFileSync as readFileSync3 } from "fs";
1722
1763
  import { mkdir as mkdir2, writeFile as writeFile2, unlink as unlink2 } from "fs/promises";
1723
- import { dirname as dirname2 } from "path";
1764
+ import { dirname } from "path";
1724
1765
  var ConfigStore = class {
1725
1766
  constructor(filePath) {
1726
1767
  this.filePath = filePath;
@@ -1752,7 +1793,7 @@ var ConfigStore = class {
1752
1793
  * config is non-secret, unlike the session file.
1753
1794
  */
1754
1795
  async save(config) {
1755
- await mkdir2(dirname2(this.filePath), { recursive: true });
1796
+ await mkdir2(dirname(this.filePath), { recursive: true });
1756
1797
  await writeFile2(this.filePath, JSON.stringify(config, null, 2) + "\n", {
1757
1798
  mode: 420
1758
1799
  });
@@ -3571,12 +3612,12 @@ function registerWorkflowExecutions(parent) {
3571
3612
  import { resolve as resolve14 } from "path";
3572
3613
 
3573
3614
  // src/lib/executionTrace.ts
3574
- import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
3615
+ import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync2 } from "fs";
3575
3616
  import { join as join3, resolve as resolve13 } from "path";
3576
3617
  function writeExecutionTraceBundle(args) {
3577
3618
  const { baseDir, trace } = args;
3578
3619
  const dir = resolve13(baseDir, trace.id);
3579
- mkdirSync2(dir, { recursive: true });
3620
+ mkdirSync3(dir, { recursive: true });
3580
3621
  const files = [];
3581
3622
  const write = (name, content) => {
3582
3623
  const path = join3(dir, name);
@@ -4118,7 +4159,7 @@ logout (or use \`geni login --server <url>\`) to switch in one step.
4118
4159
  import { homedir as homedir2 } from "os";
4119
4160
  import { join as join4 } from "path";
4120
4161
  import {
4121
- mkdirSync as mkdirSync3,
4162
+ mkdirSync as mkdirSync4,
4122
4163
  writeFileSync as writeFileSync3,
4123
4164
  existsSync as existsSync3,
4124
4165
  readFileSync as readFileSync4,
@@ -4177,7 +4218,7 @@ function installSkills() {
4177
4218
  if (!target.detect()) continue;
4178
4219
  const path = target.skillPath;
4179
4220
  try {
4180
- mkdirSync3(target.skillDir, { recursive: true });
4221
+ mkdirSync4(target.skillDir, { recursive: true });
4181
4222
  const previous = existsSync3(path) ? readFileSync4(path, "utf-8") : null;
4182
4223
  const changed = previous !== GENI_SKILL_MD;
4183
4224
  writeFileSync3(path, GENI_SKILL_MD);