@elaraai/e3-cloud-codex-plugin 1.1.2 → 1.1.4

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.
@@ -1,11 +1,11 @@
1
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/codex-hook.js
1
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/codex-hook.js
2
2
  import { readFile as readFile2 } from "node:fs/promises";
3
3
  import { existsSync, writeFileSync } from "node:fs";
4
4
  import { createHash } from "node:crypto";
5
5
  import { tmpdir } from "node:os";
6
6
  import { join, resolve as resolve3 } from "node:path";
7
7
 
8
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/codex-tools.js
8
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/codex-tools.js
9
9
  import { resolve } from "node:path";
10
10
  function patchFiles(command, cwd) {
11
11
  const files = [];
@@ -43,7 +43,7 @@ function shellReadPaths(command, cwd) {
43
43
  return [...paths];
44
44
  }
45
45
 
46
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/bash-writes.js
46
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/bash-writes.js
47
47
  import { resolve as resolve2 } from "node:path";
48
48
  function isRealPath(token) {
49
49
  if (token === "" || token.startsWith("&"))
@@ -92,7 +92,7 @@ function writtenPaths(command, cwd) {
92
92
  return [...found];
93
93
  }
94
94
 
95
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/transcript.js
95
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/transcript.js
96
96
  import { readFile } from "node:fs/promises";
97
97
  var SEARCH_TOOLS = ["mcp__plugin_east_east__search_east_examples", "mcp__plugin_east_east__get_east_example", "mcp__east__search_east_examples", "mcp__east__get_east_example"];
98
98
  async function searchedInTranscript(transcriptPath, tools = SEARCH_TOOLS) {
@@ -105,7 +105,7 @@ async function searchedInTranscript(transcriptPath, tools = SEARCH_TOOLS) {
105
105
  return tools.some((tool) => raw.includes(`"name":"${tool}"`) || raw.includes(`"name": "${tool}"`));
106
106
  }
107
107
 
108
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search-guidance.js
108
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search-guidance.js
109
109
  var EAST_GUIDANCE = {
110
110
  searchTool: "mcp__plugin_east_east__search_east_examples",
111
111
  getTool: "mcp__plugin_east_east__get_east_example",
@@ -147,7 +147,7 @@ var GATE_TEXT = gateText(EAST_GUIDANCE);
147
147
  var READ_TEXT = readText(EAST_GUIDANCE);
148
148
  var isExampleCorpusRead = corpusReadFor(EAST_GUIDANCE.scope);
149
149
 
150
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/codex-hook.js
150
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/codex-hook.js
151
151
  function codexToolCall(event) {
152
152
  const cwd = event.cwd || process.cwd();
153
153
  const input = event.tool_input ?? {};
@@ -199,7 +199,7 @@ async function codexSearchGate(event, options, call = codexToolCall(event)) {
199
199
  return context.length > 0 ? { decision: "context", text: context.join("\n\n") } : null;
200
200
  }
201
201
 
202
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/host.js
202
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/host.js
203
203
  import { existsSync as existsSync2 } from "node:fs";
204
204
  import { fileURLToPath } from "node:url";
205
205
  var isCodex = existsSync2(fileURLToPath(new URL("../../.codex-plugin/plugin.json", import.meta.url)));
@@ -207,7 +207,7 @@ function hostText(text) {
207
207
  return isCodex ? text.replaceAll("mcp__plugin_east_east__", "").replaceAll("/east:", "$east-codex-plugin:").replaceAll("Claude Code", "Codex") : text;
208
208
  }
209
209
 
210
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/hook-io.js
210
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/hook-io.js
211
211
  async function readHookInput() {
212
212
  let input = "";
213
213
  for await (const chunk of process.stdin) {
@@ -235,7 +235,7 @@ function writeHookDecision(hookEventName, decision, reason) {
235
235
  process.stdout.write(JSON.stringify(output));
236
236
  }
237
237
 
238
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search-hooks.js
238
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search-hooks.js
239
239
  function writeSearchHookReply(hookEventName, reply) {
240
240
  if (reply.decision === "deny")
241
241
  writeHookDecision(hookEventName, "deny", reply.text);
@@ -21556,7 +21556,7 @@ var StdioServerTransport = class {
21556
21556
  }
21557
21557
  };
21558
21558
 
21559
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search.js
21559
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search.js
21560
21560
  import { readFile } from "node:fs/promises";
21561
21561
 
21562
21562
  // ../../node_modules/.pnpm/minisearch@7.2.0/node_modules/minisearch/dist/es/index.js
@@ -23369,7 +23369,7 @@ var objectToNumericMapAsync = async (object3) => {
23369
23369
  var wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
23370
23370
  var SPACE_OR_PUNCTUATION = /[\n\r\p{Z}\p{P}]+/u;
23371
23371
 
23372
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search-guidance.js
23372
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search-guidance.js
23373
23373
  var EAST_GUIDANCE = {
23374
23374
  searchTool: "mcp__plugin_east_east__search_east_examples",
23375
23375
  getTool: "mcp__plugin_east_east__get_east_example",
@@ -23411,7 +23411,7 @@ var GATE_TEXT = gateText(EAST_GUIDANCE);
23411
23411
  var READ_TEXT = readText(EAST_GUIDANCE);
23412
23412
  var isExampleCorpusRead = corpusReadFor(EAST_GUIDANCE.scope);
23413
23413
 
23414
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search.js
23414
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search.js
23415
23415
  var MIN_SCORE = 5;
23416
23416
  async function loadIndexes(indexPaths) {
23417
23417
  const entries = [];
@@ -23537,7 +23537,7 @@ function getEntry(index, id) {
23537
23537
  return stored === void 0 ? null : stored;
23538
23538
  }
23539
23539
 
23540
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/mcp/example-tools.js
23540
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/mcp/example-tools.js
23541
23541
  var EAST_PACKAGES = ["east", "east-node-std", "east-node-io", "east-py-datascience", "east-ui", "e3-ui", "e3", "e3-ui-cli", "e3-create"];
23542
23542
  function isLoaded(index) {
23543
23543
  return typeof index.then === "function";
@@ -23610,7 +23610,7 @@ function registerExampleTools(server2, options) {
23610
23610
  import { fileURLToPath } from "node:url";
23611
23611
  var E3_CLOUD_INDEX_PATH = fileURLToPath(new URL("../../index.json", import.meta.url));
23612
23612
 
23613
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/transcript.js
23613
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/transcript.js
23614
23614
  var SEARCH_TOOLS = ["mcp__plugin_east_east__search_east_examples", "mcp__plugin_east_east__get_east_example", "mcp__east__search_east_examples", "mcp__east__get_east_example"];
23615
23615
 
23616
23616
  // ../e3-cloud-plugin/dist/lib/names.js
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "e3-cloud-codex-plugin",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "e3 cloud - apps for hosted e3 workspaces with @elaraai/e3-cloud-ui, files from buckets as inputs with @elaraai/e3-cloud-files, the e3-cloud CLI and @elaraai/e3-cloud-client: skills, example search and the search-first hooks",
5
5
  "author": {
6
6
  "name": "Elara AI",
package/index.json CHANGED
@@ -591,7 +591,7 @@
591
591
  "package": "e3-cloud-files",
592
592
  "file": "static",
593
593
  "suite": "Files",
594
- "test": "Declaring which inputs a bucket's files fill: a source, one file, the newest of a folder, every file of a folder and a folder's listing, each an ordinary e3.input a task takes, and the declarations e3-cloud files deploy deploys",
594
+ "test": "Declaring which inputs a source's files fill (a deployed workspace's own storage unless bound elsewhere): a source, one file, the newest of a folder, every file of a folder and a folder's listing, each an ordinary e3.input a task takes, and the declarations e3-cloud files deploy deploys",
595
595
  "keywords": [
596
596
  "Files",
597
597
  "Files.source",
@@ -606,6 +606,7 @@
606
606
  "key",
607
607
  "s3",
608
608
  "bucket",
609
+ "own storage",
609
610
  "input",
610
611
  "Blob",
611
612
  "e3-cloud-files"
@@ -615,7 +616,7 @@
615
616
  "import e3, { type Runner } from '@elaraai/e3';",
616
617
  "import { Files, FilesFolderType } from '@elaraai/e3-cloud-files';"
617
618
  ],
618
- "source": "// src/files.ts: which inputs a source's files fill. Each workspace binds the source to s3://bucket/prefix/;\n// keys and prefixes here are relative to it, so the code never names a bucket\nexport const upload = Files.source('upload');\nexport const rates = upload.file('rates', { key: 'rates.json' }); // Blob\nexport const stock = upload.latest('stock', { prefix: 'stock/', include: '*.json' }); // Blob: the greatest key, so name files by date\nexport const orders = upload.folder('orders', { prefix: 'orders/', include: '*.json' }); // Dict<String, Blob>, by path under the prefix\nexport const orderFiles = upload.listing('order_files', { prefix: 'orders/' }); // each file's size, modified and etag, no bytes\n// '*.json' is the prefix's own files; '**/*.json' takes its subfolders' too\nexport default Files.manifest([upload]);\n\n// src/package.ts: each is an ordinary input, unassigned until a sync sets it; a task decodes what arrives\nconst runner: Runner = { runtime: 'east-c', platforms: ['east-c-std'] };\nconst Rate = StructType({ currency: StringType, rate: FloatType });\nconst Order = StructType({ id: StringType, sku: StringType, amount: FloatType });\nexport const rateTable = e3.task('rate_table', [rates],\n East.function([BlobType], ArrayType(Rate), (_$, file) => file.decodeUtf8().parseJson(ArrayType(Rate))), { runner });\nexport const allOrders = e3.task('all_orders', [orders],\n East.function([FilesFolderType], ArrayType(Order), (_$, files) =>\n files.flattenToArray((_$, file) => file.decodeUtf8().parseJson(ArrayType(Order)))), { runner });\nexport default e3.package('orders', '1.0.0', rateTable, allOrders);",
619
+ "source": "// src/files.ts: which inputs a source's files fill. A deployed workspace keeps them in its own storage, upload/, unless it binds\n// the source elsewhere; keys and prefixes here are relative to where they are, so the code never names a bucket\nexport const upload = Files.source('upload');\nexport const rates = upload.file('rates', { key: 'rates.json' }); // Blob\nexport const stock = upload.latest('stock', { prefix: 'stock/', include: '*.json' }); // Blob: the greatest key, so name files by date\nexport const orders = upload.folder('orders', { prefix: 'orders/', include: '*.json' }); // Dict<String, Blob>, by path under the prefix\nexport const orderFiles = upload.listing('order_files', { prefix: 'orders/' }); // each file's size, modified and etag, no bytes\n// '*.json' is the prefix's own files; '**/*.json' takes its subfolders' too\nexport default Files.manifest([upload]);\n\n// src/package.ts: each is an ordinary input, unassigned until a sync sets it; a task decodes what arrives\nconst runner: Runner = { runtime: 'east-c', platforms: ['east-c-std'] };\nconst Rate = StructType({ currency: StringType, rate: FloatType });\nconst Order = StructType({ id: StringType, sku: StringType, amount: FloatType });\nexport const rateTable = e3.task('rate_table', [rates],\n East.function([BlobType], ArrayType(Rate), (_$, file) => file.decodeUtf8().parseJson(ArrayType(Rate))), { runner });\nexport const allOrders = e3.task('all_orders', [orders],\n East.function([FilesFolderType], ArrayType(Order), (_$, files) =>\n files.flattenToArray((_$, file) => file.decodeUtf8().parseJson(ArrayType(Order)))), { runner });\nexport default e3.package('orders', '1.0.0', rateTable, allOrders);",
619
620
  "languages": [
620
621
  "typescript"
621
622
  ]
@@ -1076,13 +1077,17 @@
1076
1077
  "package": "e3-cloud-cli",
1077
1078
  "file": "static",
1078
1079
  "suite": "e3-cloud files",
1079
- "test": "Bringing a bucket's files into a workspace: deploy the declarations, bind each source to s3://bucket/prefix/, sync what changed, then run the dataflow; locally with your own cloud profile, the bytes only in the local repository",
1080
+ "test": "Bringing files into a workspace: deploy the declarations; a deployed workspace's sources read its own storage, so push the files there with nothing to bind, or bind a source elsewhere (a path relative to the workspace such as ../production/upload/, or a bucket of your own); a local repository binds each source to s3://bucket/prefix/ and syncs with your own cloud profile, the bytes only in the local repository; then run the dataflow",
1080
1081
  "keywords": [
1081
1082
  "files",
1082
1083
  "deploy",
1083
1084
  "bind",
1084
1085
  "sync",
1086
+ "push",
1085
1087
  "status",
1088
+ "own storage",
1089
+ "path",
1090
+ "../production/upload/",
1086
1091
  "s3",
1087
1092
  "bucket",
1088
1093
  "--profile",
@@ -1094,7 +1099,7 @@
1094
1099
  "--reset"
1095
1100
  ],
1096
1101
  "imports": [],
1097
- "source": "e3 workspace deploy ./repo dev --from-source src/package.ts\ne3-cloud files deploy ./repo dev src/files.ts --dry-run # checked against the package dev runs; changes nothing\ne3-cloud files deploy ./repo dev src/files.ts\ne3-cloud files bind ./repo dev upload s3://partner-drop/orders/ --profile partner --latest 30 # locally: your own profile, each folder's 30 newest\ne3-cloud files sync ./repo dev --dry-run # what a sync would take\ne3-cloud files sync ./repo dev # reads only files whose ETag or size changed\ne3-cloud files status ./repo dev # each mapping, its type, what it holds (pending for an unseeded record), bindings, the last sync\ne3 dataflow run ./repo dev # a sync doesn't run the dataflow\n# <repo> may be a repository URL instead (https://e3.example.com/repos/acme): the same commands, run by the platform",
1102
+ "source": "# A deployed workspace: its sources read its own storage (upload/), so the declarations and a push are all it takes\ne3 workspace deploy https://e3.example.com/repos/acme dev --from-source src/package.ts\ne3-cloud files deploy https://e3.example.com/repos/acme dev src/files.ts --dry-run # checked against the package dev runs; changes nothing\ne3-cloud files deploy https://e3.example.com/repos/acme dev src/files.ts # upload reads own storage upload/: put its files there with files push\ne3-cloud files push https://e3.example.com/repos/acme dev ./data/rates.json # put with your e3 login alone, then synced\ne3-cloud files status https://e3.example.com/repos/acme dev # each mapping, its type, what it holds (pending for an unseeded record), where each source's files are, the last sync\n# A source bound elsewhere: a path relative to the workspace, or a bucket of your own the deployment grants (no location: its own folder again)\ne3-cloud files bind https://e3.example.com/repos/acme staging upload ../production/upload/ # a staging reading production's files (a push through it puts there too)\ne3-cloud files bind https://e3.example.com/repos/acme dev partner s3://partner-drop/acme/\n# A local repository has no storage of its own: bind each source to a bucket, read with your own profile\ne3 workspace deploy ./repo dev --from-source src/package.ts\ne3-cloud files deploy ./repo dev src/files.ts\ne3-cloud files bind ./repo dev upload s3://partner-drop/orders/ --profile partner --latest 30 # locally: your own profile, each folder's 30 newest\ne3-cloud files sync ./repo dev --dry-run # what a sync would take\ne3-cloud files sync ./repo dev # reads only files whose ETag or size changed\ne3 dataflow run ./repo dev # a sync doesn't run the dataflow",
1098
1103
  "languages": [
1099
1104
  "typescript"
1100
1105
  ]
@@ -1105,7 +1110,7 @@
1105
1110
  "package": "e3-cloud-cli",
1106
1111
  "file": "static",
1107
1112
  "suite": "e3-cloud files",
1108
- "test": "Pushing files from disk into a source and taking them in: each file matched to the mapping that takes it (by its name, or --as), checked against its type and limit, put with your e3 login alone where the source is the deployment's own bucket, then synced; unchanged once it is there; --dry-run plans and puts nothing",
1113
+ "test": "Pushing files from disk into a source and taking them in: each file matched to the mapping that takes it (by its name, or --as), checked against its type and limit, put with your e3 login alone where the source is the workspace's own storage (as a source bound to nothing is), then synced; unchanged once it is there; --dry-run plans and puts nothing",
1109
1114
  "keywords": [
1110
1115
  "files",
1111
1116
  "push",
@@ -1119,11 +1124,12 @@
1119
1124
  "beast2",
1120
1125
  "typed",
1121
1126
  "unchanged",
1127
+ "own storage",
1122
1128
  "dataset",
1123
1129
  "e3-cloud"
1124
1130
  ],
1125
1131
  "imports": [],
1126
- "source": "# A file by its name (a file mapping's key, or the newest under a latest prefix), a directory to a folder mapping; a row per file\ne3-cloud files push https://e3.example.com/repos/acme dev ./data/prices.beast2 ./data/deltas/2026-10-19.beast2\n# prices.beast2 → prices Array<Struct{sku, price}> 38 MiB put taken in 412,310 rows\n# 2026-10-19.beast2 → mutation:price_book.load_prices load_prices 1 MiB put committed 9f2c1a0b7e3d\n# Next: e3 dataflow run https://e3.example.com/repos/acme dev\ne3-cloud files push ./repo dev ./data/orders/ --as orders # a directory's files into the orders folder, each at its path\ne3-cloud files push ./repo dev ./data/prices.beast2 --dry-run # the plan alone: put, unchanged or refused, and why\ne3-cloud files push ./repo dev ./data/x.beast2 --profile partner # a bucket the platform does not write: your own credentials\ne3-cloud files sync ./repo dev --target prices --target mutation:price_book.load_prices # only these targets\ne3-cloud files sync ./repo dev --reset price_book # a seeded record's file taken as its state again, mutations since or not",
1132
+ "source": "# A file by its name (a file mapping's key, or the newest under a latest prefix), a directory to a folder mapping; a row per file\ne3-cloud files push https://e3.example.com/repos/acme dev ./data/prices.beast2 ./data/deltas/2026-10-19.beast2\n# upload own storage upload/\n# prices.beast2 → prices Array<Struct{sku, price}> 38 MiB put taken in 412,310 rows\n# 2026-10-19.beast2 → mutation:price_book.load_prices load_prices 1 MiB put committed 9f2c1a0b7e3d\n# Next: e3 dataflow run https://e3.example.com/repos/acme dev\ne3-cloud files push ./repo dev ./data/orders/ --as orders # a directory's files into the orders folder, each at its path\ne3-cloud files push ./repo dev ./data/prices.beast2 --dry-run # the plan alone: put, unchanged or refused, and why\ne3-cloud files push ./repo dev ./data/x.beast2 --profile partner # a bucket the platform does not write: your own credentials\ne3-cloud files sync ./repo dev --target prices --target mutation:price_book.load_prices # only these targets\ne3-cloud files sync ./repo dev --reset price_book # a seeded record's file taken as its state again, mutations since or not",
1127
1133
  "languages": [
1128
1134
  "typescript"
1129
1135
  ]
@@ -1308,7 +1314,7 @@
1308
1314
  "file": "static",
1309
1315
  "id": "e3-cloud-client:static:files",
1310
1316
  "suite": "@elaraai/e3-cloud-client files",
1311
- "test": "Files from code: deploy a workspace's declarations, bind a source, sync named targets (a seeded record reset) and wait for the sync's report, read what each mapping holds, and push a file from disk (plan, upload through the platform, sync), or a workspace's dataset as the file of its value",
1317
+ "test": "Files from code: deploy a workspace's declarations (its sources reading its own storage, so a push needs no bind), bind a source elsewhere (a path relative to the workspace, or a bucket of your own), sync named targets (a seeded record reset) and wait for the sync's report, read what each mapping holds, and push a file from disk (plan, upload through the platform, sync), or a workspace's dataset as the file of its value",
1312
1318
  "keywords": [
1313
1319
  "getFiles",
1314
1320
  "deployFiles",
@@ -1331,6 +1337,7 @@
1331
1337
  "unchanged",
1332
1338
  "refused",
1333
1339
  "retaken",
1340
+ "own storage",
1334
1341
  "workspace_protected"
1335
1342
  ],
1336
1343
  "imports": [
@@ -1341,7 +1348,7 @@
1341
1348
  "import { filesPushRefusalText, filesTargetText } from '@elaraai/e3-cloud-types';",
1342
1349
  "import manifest from './files.js'; // export default Files.manifest([upload])"
1343
1350
  ],
1344
- "source": "// The declarations (checked against the package the workspace runs), then where the source's files are\nconst deployed = await deployFiles(url, 'acme', 'staging', manifest, options);\nif (deployed.type === 'refused') throw new Error(deployed.value.problems.map((p) => p.type).join(', '));\nawait bindFilesSource(url, 'acme', 'staging', { source: 'upload', url: 's3://partner-drop/acme/', region: none, profile: none, latest: none }, options);\n\n// A sync of two targets: the seeded record's file taken as its state again (reset), and the fed mutation's new files\nconst start = await startFilesSync(url, 'acme', 'staging', {\n targets: [variant('record', 'customers'), variant('mutation', { record: 'price_book', mutation: 'load_prices' })],\n reset: ['customers'],\n}, options);\nconst id = start.value; // started, or running: the sync under way, waited on the same way\nconst job = await filesSyncWait(url, 'acme', 'staging', id, options, { deadlineMs: 10 * 60_000 });\nfor (const m of job.report.type === 'some' ? job.report.value.mappings : []) {\n console.log(filesTargetText(m.target), m.updated ? `written (commit ${m.commit.type === 'some' ? m.commit.value.slice(0, 12) : 'none'})` : 'as it was', m.refused.length === 0 ? '' : `refused ${m.refused[0]!.key}`);\n}\n\n// What each mapping holds now; a record not yet seeded is pending\nconst files = await getFiles(url, 'acme', 'staging', options);\nconsole.log(files.status.mappings.map((m) => `${filesTargetText(m.target)}: ${m.pending ? 'pending' : m.holds.type === 'some' ? m.holds.value.key : 'nothing yet'}`).join(' · '));\n\n// A push from disk: the plan says per file where it goes and how to upload it; uploadFilesPush puts it through the platform\n// (one PUT bound to its SHA-256, or a multipart upload it completes), then one sync of the targets put\nconst path = 'data/customers.beast2';\nconst size = (await stat(path)).size;\nconst plan = await planFilesPush(url, 'acme', 'staging', {\n files: [{ name: 'customers.beast2', directory: none, size: BigInt(size), sha256, type: some(customerBookType), dataset: none }], // sha256: hex; type: its beast2 header's\n as: none,\n}, options);\nconst bytes: FilesPushBytes = { size, ranged: true, read: (start) => createReadStream(path, { start }) }; // a file reads from any offset\nfor (const f of plan.files) {\n if (f.outcome.type === 'refused') console.warn(f.name, filesPushRefusalText(f.outcome.value));\n else if (f.outcome.type === 'put') await uploadFilesPush(url, 'acme', 'staging', plan, f.outcome.value, bytes, options); // an `external` put throws: your own credentials\n}\nawait startFilesSync(url, 'acme', 'staging', { targets: plan.files.flatMap((f) => (f.outcome.type === 'put' ? [f.outcome.value.target] : [])) }, options);\n// A workspace's dataset is offered as the file of its value: dataset: some({ workspace: 'dev', name: 'customers', hash }), its type the value's,\n// its bytes the value's beast2 as `e3 dataset get -f beast2` writes it, streamed (ranged: false: never held whole) and matched by its name.\n// `e3-cloud files push … dataset:dev.customers --from <repo>` does all of it",
1351
+ "source": "// The declarations (checked against the package the workspace runs). A source bound to nothing reads the workspace's own storage,\n// <source>/ (upload/ here), so a push needs no bind; no answer names the bucket that holds it\nconst deployed = await deployFiles(url, 'acme', 'staging', manifest, options);\nif (deployed.type === 'refused') throw new Error(deployed.value.problems.map((p) => p.type).join(', '));\n// A source bound elsewhere: a path relative to the workspace ('../production/upload/': production's folder, which a push through it\n// puts into too), or a bucket of your own the deployment grants; `retaken` names the targets the next sync takes again whole\nconst bound = await bindFilesSource(url, 'acme', 'staging', { source: 'partner', url: 's3://partner-drop/acme/', region: none, profile: none, latest: none }, options);\nif (bound.type === 'refused') console.warn(bound.value.map((p) => p.type).join(', '));\n\n// A sync of two targets: the seeded record's file taken as its state again (reset), and the fed mutation's new files\nconst start = await startFilesSync(url, 'acme', 'staging', {\n targets: [variant('record', 'customers'), variant('mutation', { record: 'price_book', mutation: 'load_prices' })],\n reset: ['customers'],\n}, options);\nconst id = start.value; // started, or running: the sync under way, waited on the same way\nconst job = await filesSyncWait(url, 'acme', 'staging', id, options, { deadlineMs: 10 * 60_000 });\nfor (const m of job.report.type === 'some' ? job.report.value.mappings : []) {\n console.log(filesTargetText(m.target), m.updated ? `written (commit ${m.commit.type === 'some' ? m.commit.value.slice(0, 12) : 'none'})` : 'as it was', m.refused.length === 0 ? '' : `refused ${m.refused[0]!.key}`);\n}\n\n// What each mapping holds now; a record not yet seeded is pending\nconst files = await getFiles(url, 'acme', 'staging', options);\nconsole.log(files.status.mappings.map((m) => `${filesTargetText(m.target)}: ${m.pending ? 'pending' : m.holds.type === 'some' ? m.holds.value.key : 'nothing yet'}`).join(' · '));\n\n// A push from disk: the plan says per file where it goes and how to upload it; uploadFilesPush puts it through the platform\n// (one PUT bound to its SHA-256, or a multipart upload it completes), then one sync of the targets put\nconst path = 'data/customers.beast2';\nconst size = (await stat(path)).size;\nconst plan = await planFilesPush(url, 'acme', 'staging', {\n files: [{ name: 'customers.beast2', directory: none, size: BigInt(size), sha256, type: some(customerBookType), dataset: none }], // sha256: hex; type: its beast2 header's\n as: none,\n}, options);\nconst bytes: FilesPushBytes = { size, ranged: true, read: (start) => createReadStream(path, { start }) }; // a file reads from any offset\nfor (const f of plan.files) {\n if (f.outcome.type === 'refused') console.warn(f.name, filesPushRefusalText(f.outcome.value));\n else if (f.outcome.type === 'put') await uploadFilesPush(url, 'acme', 'staging', plan, f.outcome.value, bytes, options); // an `external` put throws: your own credentials\n}\nawait startFilesSync(url, 'acme', 'staging', { targets: plan.files.flatMap((f) => (f.outcome.type === 'put' ? [f.outcome.value.target] : [])) }, options);\n// A workspace's dataset is offered as the file of its value: dataset: some({ workspace: 'dev', name: 'customers', hash }), its type the value's,\n// its bytes the value's beast2 as `e3 dataset get -f beast2` writes it, streamed (ranged: false: never held whole) and matched by its name.\n// `e3-cloud files push … dataset:dev.customers --from <repo>` does all of it",
1345
1352
  "languages": [
1346
1353
  "typescript"
1347
1354
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elaraai/e3-cloud-codex-plugin",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "e3 cloud - apps for hosted e3 workspaces with @elaraai/e3-cloud-ui, files from buckets as inputs with @elaraai/e3-cloud-files, the e3-cloud CLI and @elaraai/e3-cloud-client: skills, example search and the search-first hooks",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "type": "module",
@@ -5,13 +5,14 @@ description: "The e3-cloud CLI — run an e3 cloud deployment from the shell. Us
5
5
 
6
6
  ## Detailed skill scope
7
7
 
8
- The e3-cloud CLI — run an e3 cloud deployment from the shell. Use when: (1) Signing in (`e3 auth login` to a server, whose credentials e3-cloud shares), checking who you are and which e3 cloud release and e3 the deployment runs (`e3-cloud whoami SERVER`), and which this CLI is built with (`e3-cloud --version`, `--json`), (2) Building and deploying apps — `e3-cloud app build`, `app deploy` (UI-only, or with its package, `--dry-run`, several together, `--from-source`), `app list`, `app versions`, `app remove` (an app is developed in the cloud: deployed to a workspace of one's own and opened there), (3) Staging an app for the platform's next e3 (`app deploy --for-platform VERSION`, `app list-all`), (4) Staging and releases — `e3-cloud staging add|stop|list` (a staging protects the workspace it releases to: it then changes only by a release from it, or a rollback; `--force` on `app deploy` and `app remove` is an admin's override), `e3 workspace copy` for the rehearsal, `e3-cloud release URL FROM TO` with `--plan`, `--carry` and `--remove-apps` (a release carries the package, the apps and the Files declarations; the target keeps its data and bindings), `e3-cloud history`, `e3-cloud rollback`, (5) A repository's people and business roles — `e3-cloud user list|add|remove` (`--role owner`, or a user with `--roles 'Rostering,Finance'`), `e3-cloud role list|show|set|rm` (`--grant WORKSPACE/APP[:page,page]`), and for admins `user list-all|disable|enable|invitations|uninvite`, (6) Secrets (`e3-cloud secrets set|list|rm`), schedules (`schedule set|get|list|remove`), each task's compute size and timeout (`compute`, `timeout`), people's workspace settings (`settings`), (7) Records' history, rollback and backups (`e3-cloud records …`) and feedback (`e3-cloud feedback …`), (8) Files — a solution's inputs from files in a bucket: `e3-cloud files deploy|bind|sync|push|status`, given a local repository's path or a repository URL, and `files push` of files from disk or of a workspace's dataset as the file of its value (`dataset:WORKSPACE.NAME`, `--from` a local repository or a URL) (the e3-cloud-files skill declares and decodes them), (9) Walking a solution through its life, step by step — starting it, adding a staging, developing an app's pages, changing the package's tasks and inputs, migrating a record, Files, rehearsing and releasing, rolling back, an emergency change to production, people and roles, schedules, secrets and compute, feedback, the platform's next e3, and retiring an app or a workspace.
8
+ The e3-cloud CLI — run an e3 cloud deployment from the shell. Use when: (1) Signing in (`e3 auth login` to a server, whose credentials e3-cloud shares), checking who you are and which e3 cloud release and e3 the deployment runs (`e3-cloud whoami SERVER`), and which this CLI is built with (`e3-cloud --version`, `--json`), (2) Building and deploying apps — `e3-cloud app build`, `app deploy` (UI-only, or with its package, `--dry-run`, several together, `--from-source`), `app list`, `app versions`, `app remove` (an app is developed in the cloud: deployed to a workspace of one's own and opened there), (3) Staging an app for the platform's next e3 (`app deploy --for-platform VERSION`, `app list-all`), (4) Staging and releases — `e3-cloud staging add|stop|list` (a staging protects the workspace it releases to: it then changes only by a release from it, or a rollback; `--force` on `app deploy` and `app remove` is an admin's override), `e3 workspace copy` for the rehearsal, `e3-cloud release URL FROM TO` with `--plan`, `--carry` and `--remove-apps` (a release carries the package, the apps and the Files declarations; the target keeps its data and bindings), `e3-cloud history`, `e3-cloud rollback`, (5) A repository's people and business roles — `e3-cloud user list|add|remove` (`--role owner`, or a user with `--roles 'Rostering,Finance'`), `e3-cloud role list|show|set|rm` (`--grant WORKSPACE/APP[:page,page]`), and for admins `user list-all|disable|enable|invitations|uninvite`, (6) Secrets (`e3-cloud secrets set|list|rm`), schedules (`schedule set|get|list|remove`), each task's compute size and timeout (`compute`, `timeout`), people's workspace settings (`settings`), (7) Records' history, rollback and backups (`e3-cloud records …`) and feedback (`e3-cloud feedback …`), (8) Files — a solution's inputs from files, kept in a deployed workspace's own storage (nothing to bind, no bucket named) or a bucket of your own: `e3-cloud files deploy|bind|sync|push|status`, given a local repository's path or a repository URL, and `files push` of files from disk or of a workspace's dataset as the file of its value (`dataset:WORKSPACE.NAME`, `--from` a local repository or a URL) (the e3-cloud-files skill declares and decodes them), (9) Walking a solution through its life, step by step — starting it, adding a staging, developing an app's pages, changing the package's tasks and inputs, migrating a record, Files, rehearsing and releasing, rolling back, an emergency change to production, people and roles, schedules, secrets and compute, feedback, the platform's next e3, and retiring an app or a workspace.
9
9
 
10
10
  # e3-cloud — the e3 cloud CLI (@elaraai/e3-cloud-cli)
11
11
 
12
12
  `e3-cloud` runs the parts of an e3 cloud deployment that e3's own CLI does not:
13
13
  apps, releases, users and roles, secrets, schedules, task configs, records'
14
- history and backups, feedback, and files from buckets. e3's CLI (`e3`, `@elaraai/e3-cli`) still
14
+ history and backups, feedback, and files (a workspace's own storage, or a
15
+ bucket of your own). e3's CLI (`e3`, `@elaraai/e3-cli`) still
15
16
  does what it does against any server: repositories, packages, workspaces,
16
17
  datasets, dataflow runs and copies. Both sign in with `e3 auth login` and share its
17
18
  credentials (`~/.e3/credentials.json`).
@@ -114,9 +115,10 @@ e3-cloud staging list URL
114
115
  ```
115
116
 
116
117
  - The staging starts as a copy of production, data included: its package,
117
- inputs, records, apps, task settings and Files (declarations, bindings and
118
- what each input took). `--as-is` makes a workspace there the staging as it
119
- stands; `--replace` copies over one.
118
+ inputs, records, apps, task settings and Files declarations and bindings.
119
+ Its own storage for files starts empty, its values kept (6). `--as-is`
120
+ makes a workspace there the staging as it stands; `--replace` copies over
121
+ one.
120
122
  - Production is then protected: its package, apps and Files declarations
121
123
  change only by a release from its staging, or a rollback. People go on
122
124
  using it: inputs, records, runs and schedules.
@@ -190,21 +192,27 @@ e3-cloud release URL staging production --plan
190
192
  ### 6. Files
191
193
 
192
194
  ```bash
193
- e3-cloud files deploy URL staging src/files.ts
194
- e3-cloud files bind URL staging upload s3://bucket/acme/staging/
195
- e3-cloud files sync URL staging
195
+ e3-cloud files deploy URL staging src/files.ts # its sources read staging's own storage
196
196
  e3-cloud files push URL staging ./data/prices.beast2 ./data/customers.beast2 # from disk: matched by name (or --as), put, synced
197
197
  e3-cloud files push URL staging dataset:dev.customers --from ./repo # a local workspace's dataset, as the file of its value
198
+ e3-cloud files bind URL staging upload ../production/upload/ # or read production's files
198
199
  e3-cloud files sync URL staging --target record:customers --reset customers # named targets; a seeded record's file as its state again
199
200
  e3 dataflow run URL staging
200
201
  ```
201
202
 
203
+ - A deployed workspace keeps its sources' files in storage of its own, a
204
+ folder each (`upload/`), which `files push` puts them in with your e3 login
205
+ alone: nothing to bind, and no command names a bucket. `files bind` points
206
+ a source elsewhere: a path relative to the workspace (`../production/upload/`
207
+ is production's folder, and a push through it puts there), or
208
+ `s3://bucket/prefix/`, a bucket of your own the deployment grants.
202
209
  - The declarations (which inputs files fill) travel with releases; where
203
210
  each source's files are (its binding) is each workspace's own. A staging
204
- copied from production reads production's files until bound elsewhere.
205
- - Before a release whose declarations read a source production hasn't bound,
206
- bind it there: `files bind URL production invoices s3://…`. The release is
207
- refused until then.
211
+ copied from production keeps production's values, its own storage empty:
212
+ a push adds to them, and its first sync wipes nothing. Bind its sources to
213
+ `../production/<source>/` to rehearse on production's files.
214
+ - A source production hasn't bound reads production's own storage: push its
215
+ files there after the release, or bind it to a bucket of your own before.
208
216
  - A release syncs nothing: after it, `files sync URL production`, then run
209
217
  production. Production refuses `files deploy` (9 for the exception).
210
218
  Declaring and decoding the files: the e3-cloud-files skill.
@@ -376,7 +384,7 @@ one of its own.
376
384
  - A protected workspace refuses e3's `workspace deploy`, an app deploy or
377
385
  removal, a Files deploy, a copy onto it, and its removal or its staging's.
378
386
  Its data goes on: inputs, records, runs and schedules, and its Files
379
- bindings and syncs.
387
+ bindings, pushes and syncs.
380
388
 
381
389
  ## Releases
382
390
 
@@ -401,21 +409,27 @@ and runs next by hand or on its schedule. Owners and admins release.
401
409
 
402
410
  ## Files
403
411
 
404
- A solution declares in its code which of its inputs come from files in a
405
- bucket (`@elaraai/e3-cloud-files`, its own skill). The CLI deploys the
406
- declarations, binds each source and syncs, given a local repository's path or
407
- a repository URL:
412
+ A solution declares in its code which of its inputs come from files
413
+ (`@elaraai/e3-cloud-files`, its own skill). The CLI deploys the declarations,
414
+ pushes files, binds a source elsewhere and syncs, given a local repository's
415
+ path or a repository URL:
408
416
 
409
417
  - `files deploy <repo> <ws> <source>` runs the source (its default export is
410
418
  `Files.manifest(...)`) and checks the declarations against the package the
411
419
  workspace runs; `--dry-run` keeps nothing. A protected workspace refuses
412
420
  it: its declarations come by a release (`--force`, an admin's override).
413
- - `files bind <repo> <ws> <source> s3://bucket/prefix/` says where a source's
414
- files are; `--region` when the bucket's isn't yours. Locally only:
415
- `--profile` (the profile to read with), `--latest n` (each folder's n
416
- greatest keys). A workspace's bindings are its own: a release never
421
+ - `files bind <repo> <ws> <source> [location]` says where a source's files
422
+ are when not in the workspace's own storage under its name (a deployed
423
+ workspace's default): a path relative to the workspace (`inbound/`, `./`,
424
+ `../production/upload/`), shown as `own storage <path>`; or
425
+ `s3://bucket/prefix/`, a bucket of your own (`--region` when the bucket's
426
+ isn't yours), as every binding of a local repository is, which has no
427
+ storage of its own. No location binds it to its own folder again. Locally
428
+ only: `--profile` (the profile to read with), `--latest n` (each folder's
429
+ n greatest keys). A workspace's bindings are its own: a release never
417
430
  changes them, and a source may be bound before a release brings the
418
- declarations that read it.
431
+ declarations that read it. Bound elsewhere, the targets that took files
432
+ where it was are taken again whole by the next sync, which it names.
419
433
  - `files sync <repo> <ws>` reads only files whose ETag or size changed and
420
434
  writes only inputs whose files changed; `--target`, `--reset`, `--dry-run`. It doesn't
421
435
  run the dataflow: `e3 dataflow run` does, and the sync prints the command.
@@ -429,7 +443,9 @@ a repository URL:
429
443
  …); `--reset`, `--dry-run`, `--profile` (a bucket the platform doesn't
430
444
  write). A seed's or feed's history names the file, and the dataset it was
431
445
  pushed from.
432
- - `files status <repo> <ws>`: the declarations, the bindings, the last sync.
446
+ - `files status <repo> <ws>`: the declarations, where each source's files
447
+ are (`own storage upload/`, or a bucket's URL), what each mapping holds,
448
+ the last sync.
433
449
 
434
450
  ## Traps
435
451
 
@@ -441,8 +457,9 @@ a repository URL:
441
457
  - Sign in with `e3 auth login <server>`: there is no `e3 login`. A run is
442
458
  `e3 dataflow run <repo> <ws>`: there is no `e3 start`.
443
459
  - A release or a sync runs nothing: run the dataflow after either.
444
- - A release is refused while its Files declarations read a source the target
445
- hasn't bound: bind it in the target first.
460
+ - A source a release's Files declarations read that the target hasn't bound
461
+ reads the target's own storage: bind it in the target first when its files
462
+ are in a bucket of your own, or push them to the target after.
446
463
  - `secrets set` reads the value from stdin unless `--value` is given; the
447
464
  next run reads the new value.
448
465
  - The version is e3 cloud's own, not e3's: use the CLI of the e3 cloud
@@ -74,19 +74,28 @@ try {
74
74
  ## Files
75
75
 
76
76
  - `getFiles(url, repo, ws, options)` answers a workspace's Files: its
77
- declarations (`manifest`), where each source is bound (`bindings`), the
78
- last sync, a sync under way, and `status` — per mapping, what it holds and
79
- whether its record is `pending` (seeded from a file, its file not yet
80
- arrived).
77
+ declarations (`manifest`), where each declared source's files are
78
+ (`bindings`: a path of the workspace's own storage, relative to it, which
79
+ a source bound to nothing reads as `<source>/`; or a bucket's URL), the
80
+ last sync, a sync under way, the buckets the deployment grants besides
81
+ (`granted`), and `status` — per mapping, what it holds and whether its
82
+ record is `pending` (seeded from a file, its file not yet arrived). No
83
+ answer names the bucket that holds own storage.
81
84
  - `deployFiles(url, repo, ws, manifest, options `{ dryRun?, force? }`)` deploys declarations
82
85
  (`Files.manifest(...)` from `@elaraai/e3-cloud-files`): `deployed` with
83
86
  the targets the next sync takes again whole (`retaken`), `checked` for a
84
87
  dry run, or `refused` with every problem. A workspace with a staging
85
88
  refuses it (`workspace_protected`) unless an admin forces it.
86
89
  - `bindFilesSource(url, repo, ws, binding, options)` says where a source's
87
- files are (`s3://bucket/prefix/`): `bound` with what the next sync takes
88
- again, or `refused` (a bucket the deployment does not let the repository
89
- bind). Bindings are each workspace's own; a release never changes them.
90
+ files are when not in the workspace's own storage under its name: `url` a
91
+ path relative to the workspace (`inbound/`, `./`, `../production/upload/`;
92
+ `ownStoragePathProblem` from `@elaraai/e3-cloud-types` says why one isn't),
93
+ or `s3://bucket/prefix/`, a bucket the deployment grants. It answers
94
+ `bound` with the targets that took files where it was, which the next
95
+ sync takes again whole, or `refused` (a malformed path, a bucket the
96
+ deployment does not let the repository bind). Bindings are each
97
+ workspace's own, and relative to it: in a copy, a path names the copy's
98
+ own storage, which starts empty. A release never changes them.
90
99
  - `startFilesSync(url, repo, ws, { dryRun?, targets?, reset? }, options)`
91
100
  answers `started` with the sync's id, or `running` with the one under way;
92
101
  `filesSyncWait(url, repo, ws, id, options, { deadlineMs?, refresh?, signal? })`
@@ -1,19 +1,24 @@
1
1
  ---
2
2
  name: e3-cloud-files
3
- description: "Files for e3 cloud — a solution's inputs that come from files in a bucket (s3://bucket/prefix/), declared in its code and synced when asked, the same on a local repository and a deployed workspace. Use when: (1) Declaring which inputs a bucket's files fill with Files.source(name) and its file, latest, folder and listing, then Files.manifest([...]) as a module's default export, (2) Decoding what arrives in a task: a Blob (file, latest), a Dict of path to Blob (folder) or a listing of sizes, times and ETags, as East's JSON, CSV or text, (3) Deploying the declarations to a workspace and binding each source to s3://bucket/prefix/ with e3-cloud files deploy and bind, (4) Syncing what changed with e3-cloud files sync, locally with your own cloud profile (--profile, --latest), then running the dataflow with e3 dataflow run, (5) Reading a refusal: declarations that don't fit the package (FilesDeclarationError, a deploy's problems),… See the detailed scope below."
3
+ description: "Files for e3 cloud — a solution's inputs that come from files, declared in its code and synced when asked, the same on a local repository and a deployed workspace: in a deployed workspace's own storage (named by a path relative to the workspace, never a bucket) or a bucket of your own (s3://bucket/prefix/). Use when: (1) Declaring which inputs a bucket's files fill with Files.source(name) and its file, latest, folder and listing, then Files.manifest([...]) as a module's default export, (2) Decoding what arrives in a task: a Blob (file, latest), a Dict of path to Blob (folder) or a listing of sizes, times and ETags, as East's JSON, CSV or text, (3) Deploying the declarations to a workspace with e3-cloud files deploy, each source reading the workspace's own storage until e3-cloud files bind says otherwise (a path such as upload/ or ../production/upload/, or s3://bucket/prefix/ for a bucket of your own, which a local repository… See the detailed scope below."
4
4
  ---
5
5
 
6
6
  ## Detailed skill scope
7
7
 
8
- Files for e3 cloud — a solution's inputs that come from files in a bucket (s3://bucket/prefix/), declared in its code and synced when asked, the same on a local repository and a deployed workspace. Use when: (1) Declaring which inputs a bucket's files fill with Files.source(name) and its file, latest, folder and listing, then Files.manifest([...]) as a module's default export, (2) Decoding what arrives in a task: a Blob (file, latest), a Dict of path to Blob (folder) or a listing of sizes, times and ETags, as East's JSON, CSV or text, (3) Deploying the declarations to a workspace and binding each source to s3://bucket/prefix/ with e3-cloud files deploy and bind, (4) Syncing what changed with e3-cloud files sync, locally with your own cloud profile (--profile, --latest), then running the dataflow with e3 dataflow run, (5) Reading a refusal: declarations that don't fit the package (FilesDeclarationError, a deploy's problems), an unbound source, a file over its limit, a protected workspace (workspace_protected), (6) Moving declarations to production through staging: a release carries them with the package and apps, a rollback restores them, a copy brings bindings and what each input took, and production's bindings stay its own, (7) Reading Files' East types (FilesManifestType, FilesBindingType, FilesSyncReportType — @elaraai/e3-cloud-types), (8) Taking a beast2 file in as an input of its type with no task in between: file or latest with a type, a collection of any size, (9) Feeding a record's mutation from files with source.mutation(def, spec), def one its record declares (priceBook.mutations.load_prices), each new file applied once, (10) Pushing files from disk into a source and taking them in with e3-cloud files push, matched by name or --as, dry-run, unchanged, or a workspace's dataset (a task's output, an input, a record) as the file of its value with dataset:workspace.name and --from, local or remote at either end, (11) Seeding a record from a file with source.record(name, type, spec, parts): e3.record with no initial value, declared whole with its indexes, mutations and migrations, pending until its file, its tasks waiting meanwhile, (12) Syncing or resetting particular targets with --target and --reset.
8
+ Files for e3 cloud — a solution's inputs that come from files, declared in its code and synced when asked, the same on a local repository and a deployed workspace: in a deployed workspace's own storage (named by a path relative to the workspace, never a bucket) or a bucket of your own (s3://bucket/prefix/). Use when: (1) Declaring which inputs a bucket's files fill with Files.source(name) and its file, latest, folder and listing, then Files.manifest([...]) as a module's default export, (2) Decoding what arrives in a task: a Blob (file, latest), a Dict of path to Blob (folder) or a listing of sizes, times and ETags, as East's JSON, CSV or text, (3) Deploying the declarations to a workspace with e3-cloud files deploy, each source reading the workspace's own storage until e3-cloud files bind says otherwise (a path such as upload/ or ../production/upload/, or s3://bucket/prefix/ for a bucket of your own, which a local repository always needs), (4) Syncing what changed with e3-cloud files sync, locally with your own cloud profile (--profile, --latest), then running the dataflow with e3 dataflow run, (5) Reading a refusal: declarations that don't fit the package (FilesDeclarationError, a deploy's problems), an unbound source, a file over its limit, a protected workspace (workspace_protected), (6) Moving declarations to production through staging: a release carries them with the package and apps, a rollback restores them, a copy keeps its values and starts with empty storage of its own, and production's bindings stay its own, (7) Reading Files' East types (FilesManifestType, FilesBindingType, FilesSyncReportType — @elaraai/e3-cloud-types), (8) Taking a beast2 file in as an input of its type with no task in between: file or latest with a type, a collection of any size, (9) Feeding a record's mutation from files with source.mutation(def, spec), def one its record declares (priceBook.mutations.load_prices), each new file applied once, (10) Pushing files from disk into a source and taking them in with e3-cloud files push, matched by name or --as, dry-run, unchanged, or a workspace's dataset (a task's output, an input, a record) as the file of its value with dataset:workspace.name and --from, local or remote at either end, (11) Seeding a record from a file with source.record(name, type, spec, parts): e3.record with no initial value, declared whole with its indexes, mutations and migrations, pending until its file, its tasks waiting meanwhile, (12) Syncing or resetting particular targets with --target and --reset.
9
9
 
10
10
  # e3-cloud-files — files as e3 inputs
11
11
 
12
- A solution says in its code which of its inputs come from files in a bucket,
13
- and which records are fed from them. Each workspace says where those files
14
- are, a bucket and a prefix, and `e3-cloud files sync` brings in what changed:
15
- on a local repository with your own cloud credentials, in the cloud with the
16
- platform's read-only role. A file arrives as it is, a `Blob`, and a task
12
+ A solution says in its code which of its inputs come from files, and which
13
+ records are fed from them. A deployed workspace keeps those files in storage
14
+ of its own, a folder for each source (`upload/`), which `e3-cloud files push`
15
+ puts them in with an e3 login alone: nothing to bind, no bucket to name. A
16
+ workspace may bind a source elsewhere: another folder of its repository's
17
+ storage, by a path relative to the workspace (`../production/upload/`), or a
18
+ bucket of your own (`s3://bucket/prefix/`), which a local repository, having
19
+ no storage of its own, always names. `e3-cloud files sync` brings in what
20
+ changed: on a local repository with your own cloud credentials, in the cloud
21
+ with the platform's read-only role. A file arrives as it is, a `Blob`, and a task
17
22
  decodes it; or, named with a type, a beast2 file arrives as the value it
18
23
  holds, with no task in between. `e3-cloud files push` puts files from disk,
19
24
  or a workspace's datasets as the files of their values, into a source and
@@ -58,11 +63,15 @@ export default e3.package('orders', '1.0.0', rateTable);
58
63
  ```
59
64
 
60
65
  ```bash
61
- e3 workspace deploy ./repo dev --from-source src/package.ts
62
- e3-cloud files deploy ./repo dev src/files.ts # checked against the package dev runs
66
+ # A deployed workspace: upload reads its own storage, upload/, so nothing to bind
67
+ e3 workspace deploy $REPO dev --from-source src/package.ts # REPO=https://e3.example.com/repos/acme
68
+ e3-cloud files deploy $REPO dev src/files.ts # checked against the package dev runs
69
+ e3-cloud files push $REPO dev rates.json # into dev's own storage, then taken in
70
+ e3 dataflow run $REPO dev # the dataflow runs on it
71
+
72
+ # A local repository: no storage of its own, so bind each source to a bucket
63
73
  e3-cloud files bind ./repo dev upload s3://bucket/prefix/ --profile p # where upload's files are, for dev
64
74
  e3-cloud files sync ./repo dev # what changed comes in
65
- e3 dataflow run ./repo dev # the dataflow runs on it
66
75
  ```
67
76
 
68
77
  ## Decision Tree
@@ -70,7 +79,7 @@ e3 dataflow run ./repo dev # the da
70
79
  ```
71
80
  What do you need?
72
81
  ├─ Declare which inputs a bucket's files fill (src/files.ts)
73
- │ ├─ A place files come from → Files.source(name) — each workspace binds it to s3://bucket/prefix/
82
+ │ ├─ A place files come from → Files.source(name) — a deployed workspace's own storage, <name>/, unless bound elsewhere
74
83
  │ ├─ One known file → source.file(input, { key }) input: Blob
75
84
  │ ├─ A beast2 file as its value → source.file(input, { key, type: ArrayType(Row) }) input: Array<Row>, no task between
76
85
  │ ├─ The newest of a folder → source.latest(input, { prefix, include? }) input: Blob, the greatest key
@@ -90,8 +99,12 @@ What do you need?
90
99
  │ └─ Choose files by name or time → a listing: rows.filter(($, row) => row.modified.greaterThan(since))
91
100
  ├─ Bring the files into a workspace (the e3-cloud CLI; <repo> a local path or a repository URL)
92
101
  │ ├─ Deploy the declarations → e3-cloud files deploy <repo> <ws> src/files.ts [--dry-run]
93
- │ ├─ Say where a source's files are → e3-cloud files bind <repo> <ws> <source> s3://bucket/prefix/ [--region r]
94
- │ │ └─ Locally only → --profile <aws profile>, --latest <n> (each folder's n greatest keys)
102
+ │ ├─ Where a source's files are → a deployed workspace's own storage, <source>/: push puts them there, nothing to bind
103
+ │ ├─ Bind a source elsewhere → e3-cloud files bind <repo> <ws> <source> [path | s3://bucket/prefix/] [--region r]
104
+ │ │ ├─ Another folder of its own → a path relative to the workspace: inbound/, ./ (its folder); none: <source>/ again
105
+ │ │ ├─ Another workspace's files → ../<workspace>/<folder>/ (a staging reading production's: ../production/upload/)
106
+ │ │ ├─ A bucket of your own → s3://bucket/prefix/ (one the deployment's configuration grants the repository)
107
+ │ │ └─ A local repository → s3://bucket/prefix/ always (no storage of its own), --profile <aws profile>, --latest <n>
95
108
  │ ├─ Bring in what changed → e3-cloud files sync <repo> <ws> [--target t…] [--reset record…] [--dry-run]
96
109
  │ ├─ Put files from disk → e3-cloud files push <repo> <ws> <file|dir>… [--as target] [--dry-run] [--profile p]
97
110
  │ ├─ Push a workspace's dataset → e3-cloud files push <repo> <ws> dataset:<ws>.<name>… [--from <repo>] (a task's output, an input, a record)
@@ -99,12 +112,12 @@ What do you need?
99
112
  │ └─ Run the dataflow on it → e3 dataflow run <repo> <ws> (a sync doesn't run it)
100
113
  ├─ Production: a workspace with a staging (a deployed repository's URL)
101
114
  │ ├─ Change its declarations → files deploy to the staging, then e3-cloud release <url> <staging> <production>
102
- │ ├─ A source it doesn't bind yet → e3-cloud files bind <url> <production> <source> s3://… before the release
115
+ │ ├─ A source it doesn't bind → reads production's own storage, <source>/ (push there), unless bound elsewhere
103
116
  │ ├─ See what a release does → e3-cloud release … --plan (each input added, changed, unchanged, removed)
104
117
  │ ├─ After a release → e3-cloud files sync <url> <production>, then e3 dataflow run
105
118
  │ ├─ Undo a release → e3-cloud rollback <url> <production> (declarations go back with the package)
106
119
  │ ├─ An emergency change → e3-cloud files deploy … --force (an admin's; history records it; the next release undoes it)
107
- │ └─ Rehearse on production's files → e3-cloud staging add … (a copy: declarations, bindings, what each input took)
120
+ │ └─ Rehearse on production's files → e3-cloud staging add … (a copy: values kept, own storage empty), then bind ../production/upload/
108
121
  ├─ Keep synced inputs across a package deploy → e3 workspace deploy … --inputs keep-edited
109
122
  └─ Drive it from code
110
123
  ├─ The checks → @elaraai/e3-cloud-files/check: checkFiles, checkBindings, readPackageSurface
@@ -119,7 +132,7 @@ What do you need?
119
132
  // src/files.ts
120
133
  import { Files } from '@elaraai/e3-cloud-files';
121
134
 
122
- export const upload = Files.source('upload'); // each workspace binds it to a bucket and prefix
135
+ export const upload = Files.source('upload'); // a deployed workspace's own storage, upload/, unless bound elsewhere
123
136
 
124
137
  export const rates = upload.file('rates', { key: 'rates.json' }); // input: Blob
125
138
  export const stock = upload.latest('stock', { prefix: 'stock/', include: '*.json' }); // input: Blob, the greatest key
@@ -153,8 +166,9 @@ export default Files.manifest([upload]); // what e3-cloud files deploy dep
153
166
  bytes' hash, so the same bytes under another name are applied no more; a
154
167
  file the mutation refuses holds the ones after it until it changes; a busy
155
168
  record leaves the file to the next sync.
156
- - Keys and prefixes are relative to where a workspace binds the source; a
157
- prefix is `''` or ends with `/`.
169
+ - Keys and prefixes are relative to where the source's files are (a
170
+ deployed workspace's own `upload/`, or where it binds the source); a prefix
171
+ is `''` or ends with `/`.
158
172
  - `include` is a glob over the path under the prefix: `*` within one folder,
159
173
  `**` across folders, `?` one character, `[a-z]` one of a set (`[!_]` any
160
174
  but), `{csv,json}` any of its alternatives (`*.{csv,json}`).
@@ -179,7 +193,7 @@ file's path under the prefix, in path order.
179
193
 
180
194
  ```
181
195
  e3-cloud files deploy <repo> <workspace> src/files.ts [--dry-run]
182
- e3-cloud files bind <repo> <workspace> upload s3://bucket/prefix/ [--region r] [--profile p] [--latest n]
196
+ e3-cloud files bind <repo> <workspace> upload [path | s3://bucket/prefix/] [--region r] [--profile p] [--latest n]
183
197
  e3-cloud files sync <repo> <workspace> [--target name|record:r|mutation:r.m ...] [--reset record...] [--dry-run]
184
198
  e3-cloud files push <repo> <workspace> <file|dir|dataset:ws.name>... [--from repo] [--as target] [--reset record...] [--dry-run] [--profile p]
185
199
  e3-cloud files status <repo> <workspace>
@@ -188,9 +202,10 @@ e3 dataflow run <repo> <workspace>
188
202
 
189
203
  `files push` matches each file to the mapping that takes it (a `file` by its
190
204
  name, a `latest` under its prefix, a directory to the `folder`; `--as` names
191
- one), checks its type and size, puts it where the source is bound (the
192
- platform signs the upload into the deployment's own bucket, bound to the
193
- file's SHA-256; your own credentials elsewhere), then syncs what was put. A
205
+ one), checks its type and size, puts it where the source's files are (into
206
+ the workspace's own storage by an upload the platform signs, bound to the
207
+ file's SHA-256; into a bucket of your own with your own credentials), then
208
+ syncs what was put. A
194
209
  file the source holds already is `unchanged`; refused: `no_mapping`,
195
210
  `ambiguous` (name the target with `--as`), `not_included` (the mapping's
196
211
  glob leaves it out), `not_latest` (a greater key would shadow it), a typed
@@ -226,7 +241,11 @@ its state again, whatever mutated it since.
226
241
 
227
242
  `<repo>` is a local repository's path or a repository URL. Deploy the package
228
243
  first (`e3 workspace deploy`), then the declarations, which are checked
229
- against it. A sync reads only files whose ETag or size changed, writes only
244
+ against it. A deployed workspace's sources read its own storage until bound
245
+ elsewhere, so `files push` works after `files deploy` alone; a local
246
+ repository has none, so bind each source to a bucket before its first sync.
247
+ Own storage is named by its path alone (`own storage upload/`): no command,
248
+ history or answer names the bucket that holds it. A sync reads only files whose ETag or size changed, writes only
230
249
  inputs whose files changed, and prints the next command when any did. It
231
250
  doesn't run the dataflow: `e3 dataflow run` does. Locally the bytes land only in the
232
251
  repository; `--profile` names the profile to read with and `--latest n`
@@ -270,15 +289,19 @@ keeps only each folder's n greatest keys.
270
289
  refuses until they're deployed again. A package deploy also resets synced
271
290
  inputs unless it keeps them (`--inputs keep-edited`); the next sync takes
272
291
  them again whole.
273
- - A changed declaration, or a source bound elsewhere, has its inputs taken
274
- again whole at the next sync.
292
+ - A changed declaration has its targets taken again whole at the next sync;
293
+ so does a source bound elsewhere, for the targets that took files where it
294
+ was (`files bind` names them).
275
295
  - A workspace with a staging refuses `files deploy` (`workspace_protected`):
276
- deploy to the staging and release. A release is refused while the
277
- declarations read a source the production workspace hasn't bound: bind it
278
- there first. A release syncs nothing: sync production after it.
279
- - Bindings never travel with a release; a copy carries them. A staging
280
- started as a copy of production reads production's files until you bind it
281
- elsewhere.
296
+ deploy to the staging and release. A source production hasn't bound reads
297
+ production's own storage, `<source>/`: push its files there, or bind it
298
+ elsewhere. A release syncs nothing: sync production after it.
299
+ - Bindings never travel with a release; a copy carries them, relative to the
300
+ copy. A source in own storage (bound by a path, or to nothing) reads the
301
+ copy's own storage, which starts empty; the copy keeps the values it was
302
+ given, a file put there adds to them or replaces them, and its first sync
303
+ wipes nothing. To rehearse on production's files, bind the staging's source
304
+ to `../production/upload/`; a bucket of your own stays where it was.
282
305
  - The version is e3 cloud's own, not e3's: install the release built with the
283
306
  e3 the deployment runs, its peers (east, e3) at exactly the versions it
284
307
  names, and the `e3-cloud` CLI of the same release.