@elaraai/e3-cloud-claude-plugin 1.1.1 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.build/hooks/pre-read.js +5 -5
- package/.build/hooks/pre-write.js +6 -6
- package/.build/mcp/server.js +5 -5
- package/.claude-plugin/plugin.json +1 -1
- package/index.json +15 -8
- package/package.json +1 -1
- package/skills/e3-cloud-cli/SKILL.md +42 -25
- package/skills/e3-cloud-client/SKILL.md +16 -7
- package/skills/e3-cloud-files/SKILL.md +53 -30
package/.build/hooks/pre-read.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.
|
|
1
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_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.
|
|
9
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_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.
|
|
37
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_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.
|
|
40
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_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.
|
|
82
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_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.
|
|
1
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_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.
|
|
7
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_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.
|
|
15
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_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.
|
|
43
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_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.
|
|
56
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_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.
|
|
98
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_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;
|
package/.build/mcp/server.js
CHANGED
|
@@ -21556,7 +21556,7 @@ var StdioServerTransport = class {
|
|
|
21556
21556
|
}
|
|
21557
21557
|
};
|
|
21558
21558
|
|
|
21559
|
-
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.
|
|
21559
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_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.
|
|
23372
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_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.
|
|
23414
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_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.
|
|
23540
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_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.
|
|
23613
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_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.
|
|
3
|
+
"version": "1.1.3",
|
|
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
|
|
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.
|
|
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
|
|
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": "
|
|
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
|
|
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)
|
|
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.
|
|
3
|
+
"version": "1.1.3",
|
|
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
|
|
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
|
|
114
|
-
|
|
115
|
-
stands; `--replace` copies over
|
|
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
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
|
401
|
-
|
|
402
|
-
|
|
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>
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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,
|
|
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
|
|
441
|
-
|
|
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
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
|
84
|
-
|
|
85
|
-
|
|
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
|
|
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
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
58
|
-
e3
|
|
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) —
|
|
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
|
-
│ ├─
|
|
90
|
-
│
|
|
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
|
|
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:
|
|
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'); //
|
|
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
|
|
153
|
-
|
|
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
|
|
188
|
-
|
|
189
|
-
file's SHA-256; your own credentials
|
|
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
|
|
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
|
|
270
|
-
|
|
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
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
- Bindings never travel with a release; a copy carries them
|
|
276
|
-
|
|
277
|
-
|
|
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.
|