@elaraai/e3-cloud-claude-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,4 +1,4 @@
1
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/host.js
1
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/host.js
2
2
  import { existsSync } from "node:fs";
3
3
  import { fileURLToPath } from "node:url";
4
4
  var isCodex = existsSync(fileURLToPath(new URL("../../.codex-plugin/plugin.json", import.meta.url)));
@@ -6,7 +6,7 @@ function hostText(text) {
6
6
  return isCodex ? text.replaceAll("mcp__plugin_east_east__", "").replaceAll("/east:", "$east-codex-plugin:").replaceAll("Claude Code", "Codex") : text;
7
7
  }
8
8
 
9
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/hook-io.js
9
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/hook-io.js
10
10
  async function readHookInput() {
11
11
  let input = "";
12
12
  for await (const chunk of process.stdin) {
@@ -34,10 +34,10 @@ function writeHookDecision(hookEventName, decision, reason) {
34
34
  process.stdout.write(JSON.stringify(output));
35
35
  }
36
36
 
37
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/transcript.js
37
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/transcript.js
38
38
  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"];
39
39
 
40
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search-guidance.js
40
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search-guidance.js
41
41
  var EAST_GUIDANCE = {
42
42
  searchTool: "mcp__plugin_east_east__search_east_examples",
43
43
  getTool: "mcp__plugin_east_east__get_east_example",
@@ -79,7 +79,7 @@ var GATE_TEXT = gateText(EAST_GUIDANCE);
79
79
  var READ_TEXT = readText(EAST_GUIDANCE);
80
80
  var isExampleCorpusRead = corpusReadFor(EAST_GUIDANCE.scope);
81
81
 
82
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search-hooks.js
82
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search-hooks.js
83
83
  function preReadReminder(event, options) {
84
84
  return options.isCorpusRead(event.tool_name ?? "", event.tool_input ?? {}) ? { decision: "context", text: options.readText } : null;
85
85
  }
@@ -1,10 +1,10 @@
1
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search-hooks.js
1
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search-hooks.js
2
2
  import { readFile as readFile2 } from "node:fs/promises";
3
3
  import { existsSync as existsSync2, writeFileSync } from "node:fs";
4
4
  import { tmpdir } from "node:os";
5
5
  import { join } from "node:path";
6
6
 
7
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/host.js
7
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/host.js
8
8
  import { existsSync } from "node:fs";
9
9
  import { fileURLToPath } from "node:url";
10
10
  var isCodex = existsSync(fileURLToPath(new URL("../../.codex-plugin/plugin.json", import.meta.url)));
@@ -12,7 +12,7 @@ function hostText(text) {
12
12
  return isCodex ? text.replaceAll("mcp__plugin_east_east__", "").replaceAll("/east:", "$east-codex-plugin:").replaceAll("Claude Code", "Codex") : text;
13
13
  }
14
14
 
15
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/hook-io.js
15
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/hook-io.js
16
16
  async function readHookInput() {
17
17
  let input = "";
18
18
  for await (const chunk of process.stdin) {
@@ -40,7 +40,7 @@ function writeHookDecision(hookEventName, decision, reason) {
40
40
  process.stdout.write(JSON.stringify(output));
41
41
  }
42
42
 
43
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/transcript.js
43
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/transcript.js
44
44
  import { readFile } from "node:fs/promises";
45
45
  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"];
46
46
  async function searchedInTranscript(transcriptPath, tools = SEARCH_TOOLS) {
@@ -53,7 +53,7 @@ async function searchedInTranscript(transcriptPath, tools = SEARCH_TOOLS) {
53
53
  return tools.some((tool) => raw.includes(`"name":"${tool}"`) || raw.includes(`"name": "${tool}"`));
54
54
  }
55
55
 
56
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search-guidance.js
56
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search-guidance.js
57
57
  var EAST_GUIDANCE = {
58
58
  searchTool: "mcp__plugin_east_east__search_east_examples",
59
59
  getTool: "mcp__plugin_east_east__get_east_example",
@@ -95,7 +95,7 @@ var GATE_TEXT = gateText(EAST_GUIDANCE);
95
95
  var READ_TEXT = readText(EAST_GUIDANCE);
96
96
  var isExampleCorpusRead = corpusReadFor(EAST_GUIDANCE.scope);
97
97
 
98
- // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search-hooks.js
98
+ // ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.104_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search-hooks.js
99
99
  async function preWriteGate(event, options) {
100
100
  const cwd = event.cwd || process.cwd();
101
101
  const filePath = event.tool_input?.file_path;
@@ -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",
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-claude-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",
@@ -1,13 +1,14 @@
1
1
  ---
2
2
  name: e3-cloud-cli
3
- description: "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."
3
+ description: "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."
4
4
  ---
5
5
 
6
6
  # e3-cloud — the e3 cloud CLI (@elaraai/e3-cloud-cli)
7
7
 
8
8
  `e3-cloud` runs the parts of an e3 cloud deployment that e3's own CLI does not:
9
9
  apps, releases, users and roles, secrets, schedules, task configs, records'
10
- history and backups, feedback, and files from buckets. e3's CLI (`e3`, `@elaraai/e3-cli`) still
10
+ history and backups, feedback, and files (a workspace's own storage, or a
11
+ bucket of your own). e3's CLI (`e3`, `@elaraai/e3-cli`) still
11
12
  does what it does against any server: repositories, packages, workspaces,
12
13
  datasets, dataflow runs and copies. Both sign in with `e3 auth login` and share its
13
14
  credentials (`~/.e3/credentials.json`).
@@ -110,9 +111,10 @@ e3-cloud staging list URL
110
111
  ```
111
112
 
112
113
  - The staging starts as a copy of production, data included: its package,
113
- inputs, records, apps, task settings and Files (declarations, bindings and
114
- what each input took). `--as-is` makes a workspace there the staging as it
115
- stands; `--replace` copies over one.
114
+ inputs, records, apps, task settings and Files declarations and bindings.
115
+ Its own storage for files starts empty, its values kept (6). `--as-is`
116
+ makes a workspace there the staging as it stands; `--replace` copies over
117
+ one.
116
118
  - Production is then protected: its package, apps and Files declarations
117
119
  change only by a release from its staging, or a rollback. People go on
118
120
  using it: inputs, records, runs and schedules.
@@ -186,21 +188,27 @@ e3-cloud release URL staging production --plan
186
188
  ### 6. Files
187
189
 
188
190
  ```bash
189
- e3-cloud files deploy URL staging src/files.ts
190
- e3-cloud files bind URL staging upload s3://bucket/acme/staging/
191
- e3-cloud files sync URL staging
191
+ e3-cloud files deploy URL staging src/files.ts # its sources read staging's own storage
192
192
  e3-cloud files push URL staging ./data/prices.beast2 ./data/customers.beast2 # from disk: matched by name (or --as), put, synced
193
193
  e3-cloud files push URL staging dataset:dev.customers --from ./repo # a local workspace's dataset, as the file of its value
194
+ e3-cloud files bind URL staging upload ../production/upload/ # or read production's files
194
195
  e3-cloud files sync URL staging --target record:customers --reset customers # named targets; a seeded record's file as its state again
195
196
  e3 dataflow run URL staging
196
197
  ```
197
198
 
199
+ - A deployed workspace keeps its sources' files in storage of its own, a
200
+ folder each (`upload/`), which `files push` puts them in with your e3 login
201
+ alone: nothing to bind, and no command names a bucket. `files bind` points
202
+ a source elsewhere: a path relative to the workspace (`../production/upload/`
203
+ is production's folder, and a push through it puts there), or
204
+ `s3://bucket/prefix/`, a bucket of your own the deployment grants.
198
205
  - The declarations (which inputs files fill) travel with releases; where
199
206
  each source's files are (its binding) is each workspace's own. A staging
200
- copied from production reads production's files until bound elsewhere.
201
- - Before a release whose declarations read a source production hasn't bound,
202
- bind it there: `files bind URL production invoices s3://…`. The release is
203
- refused until then.
207
+ copied from production keeps production's values, its own storage empty:
208
+ a push adds to them, and its first sync wipes nothing. Bind its sources to
209
+ `../production/<source>/` to rehearse on production's files.
210
+ - A source production hasn't bound reads production's own storage: push its
211
+ files there after the release, or bind it to a bucket of your own before.
204
212
  - A release syncs nothing: after it, `files sync URL production`, then run
205
213
  production. Production refuses `files deploy` (9 for the exception).
206
214
  Declaring and decoding the files: the e3-cloud-files skill.
@@ -372,7 +380,7 @@ one of its own.
372
380
  - A protected workspace refuses e3's `workspace deploy`, an app deploy or
373
381
  removal, a Files deploy, a copy onto it, and its removal or its staging's.
374
382
  Its data goes on: inputs, records, runs and schedules, and its Files
375
- bindings and syncs.
383
+ bindings, pushes and syncs.
376
384
 
377
385
  ## Releases
378
386
 
@@ -397,21 +405,27 @@ and runs next by hand or on its schedule. Owners and admins release.
397
405
 
398
406
  ## Files
399
407
 
400
- A solution declares in its code which of its inputs come from files in a
401
- bucket (`@elaraai/e3-cloud-files`, its own skill). The CLI deploys the
402
- declarations, binds each source and syncs, given a local repository's path or
403
- a repository URL:
408
+ A solution declares in its code which of its inputs come from files
409
+ (`@elaraai/e3-cloud-files`, its own skill). The CLI deploys the declarations,
410
+ pushes files, binds a source elsewhere and syncs, given a local repository's
411
+ path or a repository URL:
404
412
 
405
413
  - `files deploy <repo> <ws> <source>` runs the source (its default export is
406
414
  `Files.manifest(...)`) and checks the declarations against the package the
407
415
  workspace runs; `--dry-run` keeps nothing. A protected workspace refuses
408
416
  it: its declarations come by a release (`--force`, an admin's override).
409
- - `files bind <repo> <ws> <source> s3://bucket/prefix/` says where a source's
410
- files are; `--region` when the bucket's isn't yours. Locally only:
411
- `--profile` (the profile to read with), `--latest n` (each folder's n
412
- greatest keys). A workspace's bindings are its own: a release never
417
+ - `files bind <repo> <ws> <source> [location]` says where a source's files
418
+ are when not in the workspace's own storage under its name (a deployed
419
+ workspace's default): a path relative to the workspace (`inbound/`, `./`,
420
+ `../production/upload/`), shown as `own storage <path>`; or
421
+ `s3://bucket/prefix/`, a bucket of your own (`--region` when the bucket's
422
+ isn't yours), as every binding of a local repository is, which has no
423
+ storage of its own. No location binds it to its own folder again. Locally
424
+ only: `--profile` (the profile to read with), `--latest n` (each folder's
425
+ n greatest keys). A workspace's bindings are its own: a release never
413
426
  changes them, and a source may be bound before a release brings the
414
- declarations that read it.
427
+ declarations that read it. Bound elsewhere, the targets that took files
428
+ where it was are taken again whole by the next sync, which it names.
415
429
  - `files sync <repo> <ws>` reads only files whose ETag or size changed and
416
430
  writes only inputs whose files changed; `--target`, `--reset`, `--dry-run`. It doesn't
417
431
  run the dataflow: `e3 dataflow run` does, and the sync prints the command.
@@ -425,7 +439,9 @@ a repository URL:
425
439
  …); `--reset`, `--dry-run`, `--profile` (a bucket the platform doesn't
426
440
  write). A seed's or feed's history names the file, and the dataset it was
427
441
  pushed from.
428
- - `files status <repo> <ws>`: the declarations, the bindings, the last sync.
442
+ - `files status <repo> <ws>`: the declarations, where each source's files
443
+ are (`own storage upload/`, or a bucket's URL), what each mapping holds,
444
+ the last sync.
429
445
 
430
446
  ## Traps
431
447
 
@@ -437,8 +453,9 @@ a repository URL:
437
453
  - Sign in with `e3 auth login <server>`: there is no `e3 login`. A run is
438
454
  `e3 dataflow run <repo> <ws>`: there is no `e3 start`.
439
455
  - A release or a sync runs nothing: run the dataflow after either.
440
- - A release is refused while its Files declarations read a source the target
441
- hasn't bound: bind it in the target first.
456
+ - A source a release's Files declarations read that the target hasn't bound
457
+ reads the target's own storage: bind it in the target first when its files
458
+ are in a bucket of your own, or push them to the target after.
442
459
  - `secrets set` reads the value from stdin unless `--value` is given; the
443
460
  next run reads the new value.
444
461
  - The version is e3 cloud's own, not e3's: use the CLI of the e3 cloud
@@ -70,19 +70,28 @@ try {
70
70
  ## Files
71
71
 
72
72
  - `getFiles(url, repo, ws, options)` answers a workspace's Files: its
73
- declarations (`manifest`), where each source is bound (`bindings`), the
74
- last sync, a sync under way, and `status` — per mapping, what it holds and
75
- whether its record is `pending` (seeded from a file, its file not yet
76
- arrived).
73
+ declarations (`manifest`), where each declared source's files are
74
+ (`bindings`: a path of the workspace's own storage, relative to it, which
75
+ a source bound to nothing reads as `<source>/`; or a bucket's URL), the
76
+ last sync, a sync under way, the buckets the deployment grants besides
77
+ (`granted`), and `status` — per mapping, what it holds and whether its
78
+ record is `pending` (seeded from a file, its file not yet arrived). No
79
+ answer names the bucket that holds own storage.
77
80
  - `deployFiles(url, repo, ws, manifest, options `{ dryRun?, force? }`)` deploys declarations
78
81
  (`Files.manifest(...)` from `@elaraai/e3-cloud-files`): `deployed` with
79
82
  the targets the next sync takes again whole (`retaken`), `checked` for a
80
83
  dry run, or `refused` with every problem. A workspace with a staging
81
84
  refuses it (`workspace_protected`) unless an admin forces it.
82
85
  - `bindFilesSource(url, repo, ws, binding, options)` says where a source's
83
- files are (`s3://bucket/prefix/`): `bound` with what the next sync takes
84
- again, or `refused` (a bucket the deployment does not let the repository
85
- bind). Bindings are each workspace's own; a release never changes them.
86
+ files are when not in the workspace's own storage under its name: `url` a
87
+ path relative to the workspace (`inbound/`, `./`, `../production/upload/`;
88
+ `ownStoragePathProblem` from `@elaraai/e3-cloud-types` says why one isn't),
89
+ or `s3://bucket/prefix/`, a bucket the deployment grants. It answers
90
+ `bound` with the targets that took files where it was, which the next
91
+ sync takes again whole, or `refused` (a malformed path, a bucket the
92
+ deployment does not let the repository bind). Bindings are each
93
+ workspace's own, and relative to it: in a copy, a path names the copy's
94
+ own storage, which starts empty. A release never changes them.
86
95
  - `startFilesSync(url, repo, ws, { dryRun?, targets?, reset? }, options)`
87
96
  answers `started` with the sync's id, or `running` with the one under way;
88
97
  `filesSyncWait(url, repo, ws, id, options, { deadlineMs?, refresh?, signal? })`
@@ -1,15 +1,20 @@
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), 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."
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 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."
4
4
  ---
5
5
 
6
6
  # e3-cloud-files — files as e3 inputs
7
7
 
8
- A solution says in its code which of its inputs come from files in a bucket,
9
- and which records are fed from them. Each workspace says where those files
10
- are, a bucket and a prefix, and `e3-cloud files sync` brings in what changed:
11
- on a local repository with your own cloud credentials, in the cloud with the
12
- platform's read-only role. A file arrives as it is, a `Blob`, and a task
8
+ A solution says in its code which of its inputs come from files, and which
9
+ records are fed from them. A deployed workspace keeps those files in storage
10
+ of its own, a folder for each source (`upload/`), which `e3-cloud files push`
11
+ puts them in with an e3 login alone: nothing to bind, no bucket to name. A
12
+ workspace may bind a source elsewhere: another folder of its repository's
13
+ storage, by a path relative to the workspace (`../production/upload/`), or a
14
+ bucket of your own (`s3://bucket/prefix/`), which a local repository, having
15
+ no storage of its own, always names. `e3-cloud files sync` brings in what
16
+ changed: on a local repository with your own cloud credentials, in the cloud
17
+ with the platform's read-only role. A file arrives as it is, a `Blob`, and a task
13
18
  decodes it; or, named with a type, a beast2 file arrives as the value it
14
19
  holds, with no task in between. `e3-cloud files push` puts files from disk,
15
20
  or a workspace's datasets as the files of their values, into a source and
@@ -54,11 +59,15 @@ export default e3.package('orders', '1.0.0', rateTable);
54
59
  ```
55
60
 
56
61
  ```bash
57
- e3 workspace deploy ./repo dev --from-source src/package.ts
58
- e3-cloud files deploy ./repo dev src/files.ts # checked against the package dev runs
62
+ # A deployed workspace: upload reads its own storage, upload/, so nothing to bind
63
+ e3 workspace deploy $REPO dev --from-source src/package.ts # REPO=https://e3.example.com/repos/acme
64
+ e3-cloud files deploy $REPO dev src/files.ts # checked against the package dev runs
65
+ e3-cloud files push $REPO dev rates.json # into dev's own storage, then taken in
66
+ e3 dataflow run $REPO dev # the dataflow runs on it
67
+
68
+ # A local repository: no storage of its own, so bind each source to a bucket
59
69
  e3-cloud files bind ./repo dev upload s3://bucket/prefix/ --profile p # where upload's files are, for dev
60
70
  e3-cloud files sync ./repo dev # what changed comes in
61
- e3 dataflow run ./repo dev # the dataflow runs on it
62
71
  ```
63
72
 
64
73
  ## Decision Tree
@@ -66,7 +75,7 @@ e3 dataflow run ./repo dev # the da
66
75
  ```
67
76
  What do you need?
68
77
  ├─ Declare which inputs a bucket's files fill (src/files.ts)
69
- │ ├─ A place files come from → Files.source(name) — each workspace binds it to s3://bucket/prefix/
78
+ │ ├─ A place files come from → Files.source(name) — a deployed workspace's own storage, <name>/, unless bound elsewhere
70
79
  │ ├─ One known file → source.file(input, { key }) input: Blob
71
80
  │ ├─ A beast2 file as its value → source.file(input, { key, type: ArrayType(Row) }) input: Array<Row>, no task between
72
81
  │ ├─ The newest of a folder → source.latest(input, { prefix, include? }) input: Blob, the greatest key
@@ -86,8 +95,12 @@ What do you need?
86
95
  │ └─ Choose files by name or time → a listing: rows.filter(($, row) => row.modified.greaterThan(since))
87
96
  ├─ Bring the files into a workspace (the e3-cloud CLI; <repo> a local path or a repository URL)
88
97
  │ ├─ Deploy the declarations → e3-cloud files deploy <repo> <ws> src/files.ts [--dry-run]
89
- │ ├─ Say where a source's files are → e3-cloud files bind <repo> <ws> <source> s3://bucket/prefix/ [--region r]
90
- │ │ └─ Locally only → --profile <aws profile>, --latest <n> (each folder's n greatest keys)
98
+ │ ├─ Where a source's files are → a deployed workspace's own storage, <source>/: push puts them there, nothing to bind
99
+ │ ├─ Bind a source elsewhere → e3-cloud files bind <repo> <ws> <source> [path | s3://bucket/prefix/] [--region r]
100
+ │ │ ├─ Another folder of its own → a path relative to the workspace: inbound/, ./ (its folder); none: <source>/ again
101
+ │ │ ├─ Another workspace's files → ../<workspace>/<folder>/ (a staging reading production's: ../production/upload/)
102
+ │ │ ├─ A bucket of your own → s3://bucket/prefix/ (one the deployment's configuration grants the repository)
103
+ │ │ └─ A local repository → s3://bucket/prefix/ always (no storage of its own), --profile <aws profile>, --latest <n>
91
104
  │ ├─ Bring in what changed → e3-cloud files sync <repo> <ws> [--target t…] [--reset record…] [--dry-run]
92
105
  │ ├─ Put files from disk → e3-cloud files push <repo> <ws> <file|dir>… [--as target] [--dry-run] [--profile p]
93
106
  │ ├─ Push a workspace's dataset → e3-cloud files push <repo> <ws> dataset:<ws>.<name>… [--from <repo>] (a task's output, an input, a record)
@@ -95,12 +108,12 @@ What do you need?
95
108
  │ └─ Run the dataflow on it → e3 dataflow run <repo> <ws> (a sync doesn't run it)
96
109
  ├─ Production: a workspace with a staging (a deployed repository's URL)
97
110
  │ ├─ Change its declarations → files deploy to the staging, then e3-cloud release <url> <staging> <production>
98
- │ ├─ A source it doesn't bind yet → e3-cloud files bind <url> <production> <source> s3://… before the release
111
+ │ ├─ A source it doesn't bind → reads production's own storage, <source>/ (push there), unless bound elsewhere
99
112
  │ ├─ See what a release does → e3-cloud release … --plan (each input added, changed, unchanged, removed)
100
113
  │ ├─ After a release → e3-cloud files sync <url> <production>, then e3 dataflow run
101
114
  │ ├─ Undo a release → e3-cloud rollback <url> <production> (declarations go back with the package)
102
115
  │ ├─ An emergency change → e3-cloud files deploy … --force (an admin's; history records it; the next release undoes it)
103
- │ └─ Rehearse on production's files → e3-cloud staging add … (a copy: declarations, bindings, what each input took)
116
+ │ └─ Rehearse on production's files → e3-cloud staging add … (a copy: values kept, own storage empty), then bind ../production/upload/
104
117
  ├─ Keep synced inputs across a package deploy → e3 workspace deploy … --inputs keep-edited
105
118
  └─ Drive it from code
106
119
  ├─ The checks → @elaraai/e3-cloud-files/check: checkFiles, checkBindings, readPackageSurface
@@ -115,7 +128,7 @@ What do you need?
115
128
  // src/files.ts
116
129
  import { Files } from '@elaraai/e3-cloud-files';
117
130
 
118
- export const upload = Files.source('upload'); // each workspace binds it to a bucket and prefix
131
+ export const upload = Files.source('upload'); // a deployed workspace's own storage, upload/, unless bound elsewhere
119
132
 
120
133
  export const rates = upload.file('rates', { key: 'rates.json' }); // input: Blob
121
134
  export const stock = upload.latest('stock', { prefix: 'stock/', include: '*.json' }); // input: Blob, the greatest key
@@ -149,8 +162,9 @@ export default Files.manifest([upload]); // what e3-cloud files deploy dep
149
162
  bytes' hash, so the same bytes under another name are applied no more; a
150
163
  file the mutation refuses holds the ones after it until it changes; a busy
151
164
  record leaves the file to the next sync.
152
- - Keys and prefixes are relative to where a workspace binds the source; a
153
- prefix is `''` or ends with `/`.
165
+ - Keys and prefixes are relative to where the source's files are (a
166
+ deployed workspace's own `upload/`, or where it binds the source); a prefix
167
+ is `''` or ends with `/`.
154
168
  - `include` is a glob over the path under the prefix: `*` within one folder,
155
169
  `**` across folders, `?` one character, `[a-z]` one of a set (`[!_]` any
156
170
  but), `{csv,json}` any of its alternatives (`*.{csv,json}`).
@@ -175,7 +189,7 @@ file's path under the prefix, in path order.
175
189
 
176
190
  ```
177
191
  e3-cloud files deploy <repo> <workspace> src/files.ts [--dry-run]
178
- e3-cloud files bind <repo> <workspace> upload s3://bucket/prefix/ [--region r] [--profile p] [--latest n]
192
+ e3-cloud files bind <repo> <workspace> upload [path | s3://bucket/prefix/] [--region r] [--profile p] [--latest n]
179
193
  e3-cloud files sync <repo> <workspace> [--target name|record:r|mutation:r.m ...] [--reset record...] [--dry-run]
180
194
  e3-cloud files push <repo> <workspace> <file|dir|dataset:ws.name>... [--from repo] [--as target] [--reset record...] [--dry-run] [--profile p]
181
195
  e3-cloud files status <repo> <workspace>
@@ -184,9 +198,10 @@ e3 dataflow run <repo> <workspace>
184
198
 
185
199
  `files push` matches each file to the mapping that takes it (a `file` by its
186
200
  name, a `latest` under its prefix, a directory to the `folder`; `--as` names
187
- one), checks its type and size, puts it where the source is bound (the
188
- platform signs the upload into the deployment's own bucket, bound to the
189
- file's SHA-256; your own credentials elsewhere), then syncs what was put. A
201
+ one), checks its type and size, puts it where the source's files are (into
202
+ the workspace's own storage by an upload the platform signs, bound to the
203
+ file's SHA-256; into a bucket of your own with your own credentials), then
204
+ syncs what was put. A
190
205
  file the source holds already is `unchanged`; refused: `no_mapping`,
191
206
  `ambiguous` (name the target with `--as`), `not_included` (the mapping's
192
207
  glob leaves it out), `not_latest` (a greater key would shadow it), a typed
@@ -222,7 +237,11 @@ its state again, whatever mutated it since.
222
237
 
223
238
  `<repo>` is a local repository's path or a repository URL. Deploy the package
224
239
  first (`e3 workspace deploy`), then the declarations, which are checked
225
- against it. A sync reads only files whose ETag or size changed, writes only
240
+ against it. A deployed workspace's sources read its own storage until bound
241
+ elsewhere, so `files push` works after `files deploy` alone; a local
242
+ repository has none, so bind each source to a bucket before its first sync.
243
+ Own storage is named by its path alone (`own storage upload/`): no command,
244
+ history or answer names the bucket that holds it. A sync reads only files whose ETag or size changed, writes only
226
245
  inputs whose files changed, and prints the next command when any did. It
227
246
  doesn't run the dataflow: `e3 dataflow run` does. Locally the bytes land only in the
228
247
  repository; `--profile` names the profile to read with and `--latest n`
@@ -266,15 +285,19 @@ keeps only each folder's n greatest keys.
266
285
  refuses until they're deployed again. A package deploy also resets synced
267
286
  inputs unless it keeps them (`--inputs keep-edited`); the next sync takes
268
287
  them again whole.
269
- - A changed declaration, or a source bound elsewhere, has its inputs taken
270
- again whole at the next sync.
288
+ - A changed declaration has its targets taken again whole at the next sync;
289
+ so does a source bound elsewhere, for the targets that took files where it
290
+ was (`files bind` names them).
271
291
  - A workspace with a staging refuses `files deploy` (`workspace_protected`):
272
- deploy to the staging and release. A release is refused while the
273
- declarations read a source the production workspace hasn't bound: bind it
274
- there first. A release syncs nothing: sync production after it.
275
- - Bindings never travel with a release; a copy carries them. A staging
276
- started as a copy of production reads production's files until you bind it
277
- elsewhere.
292
+ deploy to the staging and release. A source production hasn't bound reads
293
+ production's own storage, `<source>/`: push its files there, or bind it
294
+ elsewhere. A release syncs nothing: sync production after it.
295
+ - Bindings never travel with a release; a copy carries them, relative to the
296
+ copy. A source in own storage (bound by a path, or to nothing) reads the
297
+ copy's own storage, which starts empty; the copy keeps the values it was
298
+ given, a file put there adds to them or replaces them, and its first sync
299
+ wipes nothing. To rehearse on production's files, bind the staging's source
300
+ to `../production/upload/`; a bucket of your own stays where it was.
278
301
  - The version is e3 cloud's own, not e3's: install the release built with the
279
302
  e3 the deployment runs, its peers (east, e3) at exactly the versions it
280
303
  names, and the `e3-cloud` CLI of the same release.