@elaraai/e3-cloud-codex-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/codex-tools.js +9 -9
- package/.build/mcp/server.js +5 -5
- package/.codex-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 +54 -31
|
@@ -1,11 +1,11 @@
|
|
|
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/codex-hook.js
|
|
2
2
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
3
3
|
import { existsSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import { join, resolve as resolve3 } from "node:path";
|
|
7
7
|
|
|
8
|
-
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.
|
|
8
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/codex-tools.js
|
|
9
9
|
import { resolve } from "node:path";
|
|
10
10
|
function patchFiles(command, cwd) {
|
|
11
11
|
const files = [];
|
|
@@ -43,7 +43,7 @@ function shellReadPaths(command, cwd) {
|
|
|
43
43
|
return [...paths];
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.
|
|
46
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/bash-writes.js
|
|
47
47
|
import { resolve as resolve2 } from "node:path";
|
|
48
48
|
function isRealPath(token) {
|
|
49
49
|
if (token === "" || token.startsWith("&"))
|
|
@@ -92,7 +92,7 @@ function writtenPaths(command, cwd) {
|
|
|
92
92
|
return [...found];
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.
|
|
95
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/transcript.js
|
|
96
96
|
import { readFile } from "node:fs/promises";
|
|
97
97
|
var SEARCH_TOOLS = ["mcp__plugin_east_east__search_east_examples", "mcp__plugin_east_east__get_east_example", "mcp__east__search_east_examples", "mcp__east__get_east_example"];
|
|
98
98
|
async function searchedInTranscript(transcriptPath, tools = SEARCH_TOOLS) {
|
|
@@ -105,7 +105,7 @@ async function searchedInTranscript(transcriptPath, tools = SEARCH_TOOLS) {
|
|
|
105
105
|
return tools.some((tool) => raw.includes(`"name":"${tool}"`) || raw.includes(`"name": "${tool}"`));
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.
|
|
108
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search-guidance.js
|
|
109
109
|
var EAST_GUIDANCE = {
|
|
110
110
|
searchTool: "mcp__plugin_east_east__search_east_examples",
|
|
111
111
|
getTool: "mcp__plugin_east_east__get_east_example",
|
|
@@ -147,7 +147,7 @@ var GATE_TEXT = gateText(EAST_GUIDANCE);
|
|
|
147
147
|
var READ_TEXT = readText(EAST_GUIDANCE);
|
|
148
148
|
var isExampleCorpusRead = corpusReadFor(EAST_GUIDANCE.scope);
|
|
149
149
|
|
|
150
|
-
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.
|
|
150
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/codex-hook.js
|
|
151
151
|
function codexToolCall(event) {
|
|
152
152
|
const cwd = event.cwd || process.cwd();
|
|
153
153
|
const input = event.tool_input ?? {};
|
|
@@ -199,7 +199,7 @@ async function codexSearchGate(event, options, call = codexToolCall(event)) {
|
|
|
199
199
|
return context.length > 0 ? { decision: "context", text: context.join("\n\n") } : null;
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
-
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.
|
|
202
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/host.js
|
|
203
203
|
import { existsSync as existsSync2 } from "node:fs";
|
|
204
204
|
import { fileURLToPath } from "node:url";
|
|
205
205
|
var isCodex = existsSync2(fileURLToPath(new URL("../../.codex-plugin/plugin.json", import.meta.url)));
|
|
@@ -207,7 +207,7 @@ function hostText(text) {
|
|
|
207
207
|
return isCodex ? text.replaceAll("mcp__plugin_east_east__", "").replaceAll("/east:", "$east-codex-plugin:").replaceAll("Claude Code", "Codex") : text;
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.
|
|
210
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/hook-io.js
|
|
211
211
|
async function readHookInput() {
|
|
212
212
|
let input = "";
|
|
213
213
|
for await (const chunk of process.stdin) {
|
|
@@ -235,7 +235,7 @@ function writeHookDecision(hookEventName, decision, reason) {
|
|
|
235
235
|
process.stdout.write(JSON.stringify(output));
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
-
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.
|
|
238
|
+
// ../../node_modules/.pnpm/@elaraai+east-plugin@1.0.102_typescript@5.9.3/node_modules/@elaraai/east-plugin/dist/lib/search-hooks.js
|
|
239
239
|
function writeSearchHookReply(hookEventName, reply) {
|
|
240
240
|
if (reply.decision === "deny")
|
|
241
241
|
writeHookDecision(hookEventName, "deny", reply.text);
|
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-codex-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
|
"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-codex-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",
|
|
@@ -5,13 +5,14 @@ description: "The e3-cloud CLI — run an e3 cloud deployment from the shell. Us
|
|
|
5
5
|
|
|
6
6
|
## Detailed skill scope
|
|
7
7
|
|
|
8
|
-
The e3-cloud CLI — run an e3 cloud deployment from the shell. Use when: (1) Signing in (`e3 auth login` to a server, whose credentials e3-cloud shares), checking who you are and which e3 cloud release and e3 the deployment runs (`e3-cloud whoami SERVER`), and which this CLI is built with (`e3-cloud --version`, `--json`), (2) Building and deploying apps — `e3-cloud app build`, `app deploy` (UI-only, or with its package, `--dry-run`, several together, `--from-source`), `app list`, `app versions`, `app remove` (an app is developed in the cloud: deployed to a workspace of one's own and opened there), (3) Staging an app for the platform's next e3 (`app deploy --for-platform VERSION`, `app list-all`), (4) Staging and releases — `e3-cloud staging add|stop|list` (a staging protects the workspace it releases to: it then changes only by a release from it, or a rollback; `--force` on `app deploy` and `app remove` is an admin's override), `e3 workspace copy` for the rehearsal, `e3-cloud release URL FROM TO` with `--plan`, `--carry` and `--remove-apps` (a release carries the package, the apps and the Files declarations; the target keeps its data and bindings), `e3-cloud history`, `e3-cloud rollback`, (5) A repository's people and business roles — `e3-cloud user list|add|remove` (`--role owner`, or a user with `--roles 'Rostering,Finance'`), `e3-cloud role list|show|set|rm` (`--grant WORKSPACE/APP[:page,page]`), and for admins `user list-all|disable|enable|invitations|uninvite`, (6) Secrets (`e3-cloud secrets set|list|rm`), schedules (`schedule set|get|list|remove`), each task's compute size and timeout (`compute`, `timeout`), people's workspace settings (`settings`), (7) Records' history, rollback and backups (`e3-cloud records …`) and feedback (`e3-cloud feedback …`), (8) Files — a solution's inputs from files in a bucket: `e3-cloud files deploy|bind|sync|push|status`, given a local repository's path or a repository URL, and `files push` of files from disk or of a workspace's dataset as the file of its value (`dataset:WORKSPACE.NAME`, `--from` a local repository or a URL) (the e3-cloud-files skill declares and decodes them), (9) Walking a solution through its life, step by step — starting it, adding a staging, developing an app's pages, changing the package's tasks and inputs, migrating a record, Files, rehearsing and releasing, rolling back, an emergency change to production, people and roles, schedules, secrets and compute, feedback, the platform's next e3, and retiring an app or a workspace.
|
|
8
|
+
The e3-cloud CLI — run an e3 cloud deployment from the shell. Use when: (1) Signing in (`e3 auth login` to a server, whose credentials e3-cloud shares), checking who you are and which e3 cloud release and e3 the deployment runs (`e3-cloud whoami SERVER`), and which this CLI is built with (`e3-cloud --version`, `--json`), (2) Building and deploying apps — `e3-cloud app build`, `app deploy` (UI-only, or with its package, `--dry-run`, several together, `--from-source`), `app list`, `app versions`, `app remove` (an app is developed in the cloud: deployed to a workspace of one's own and opened there), (3) Staging an app for the platform's next e3 (`app deploy --for-platform VERSION`, `app list-all`), (4) Staging and releases — `e3-cloud staging add|stop|list` (a staging protects the workspace it releases to: it then changes only by a release from it, or a rollback; `--force` on `app deploy` and `app remove` is an admin's override), `e3 workspace copy` for the rehearsal, `e3-cloud release URL FROM TO` with `--plan`, `--carry` and `--remove-apps` (a release carries the package, the apps and the Files declarations; the target keeps its data and bindings), `e3-cloud history`, `e3-cloud rollback`, (5) A repository's people and business roles — `e3-cloud user list|add|remove` (`--role owner`, or a user with `--roles 'Rostering,Finance'`), `e3-cloud role list|show|set|rm` (`--grant WORKSPACE/APP[:page,page]`), and for admins `user list-all|disable|enable|invitations|uninvite`, (6) Secrets (`e3-cloud secrets set|list|rm`), schedules (`schedule set|get|list|remove`), each task's compute size and timeout (`compute`, `timeout`), people's workspace settings (`settings`), (7) Records' history, rollback and backups (`e3-cloud records …`) and feedback (`e3-cloud feedback …`), (8) Files — a solution's inputs from files, kept in a deployed workspace's own storage (nothing to bind, no bucket named) or a bucket of your own: `e3-cloud files deploy|bind|sync|push|status`, given a local repository's path or a repository URL, and `files push` of files from disk or of a workspace's dataset as the file of its value (`dataset:WORKSPACE.NAME`, `--from` a local repository or a URL) (the e3-cloud-files skill declares and decodes them), (9) Walking a solution through its life, step by step — starting it, adding a staging, developing an app's pages, changing the package's tasks and inputs, migrating a record, Files, rehearsing and releasing, rolling back, an emergency change to production, people and roles, schedules, secrets and compute, feedback, the platform's next e3, and retiring an app or a workspace.
|
|
9
9
|
|
|
10
10
|
# e3-cloud — the e3 cloud CLI (@elaraai/e3-cloud-cli)
|
|
11
11
|
|
|
12
12
|
`e3-cloud` runs the parts of an e3 cloud deployment that e3's own CLI does not:
|
|
13
13
|
apps, releases, users and roles, secrets, schedules, task configs, records'
|
|
14
|
-
history and backups, feedback, and files
|
|
14
|
+
history and backups, feedback, and files (a workspace's own storage, or a
|
|
15
|
+
bucket of your own). e3's CLI (`e3`, `@elaraai/e3-cli`) still
|
|
15
16
|
does what it does against any server: repositories, packages, workspaces,
|
|
16
17
|
datasets, dataflow runs and copies. Both sign in with `e3 auth login` and share its
|
|
17
18
|
credentials (`~/.e3/credentials.json`).
|
|
@@ -114,9 +115,10 @@ e3-cloud staging list URL
|
|
|
114
115
|
```
|
|
115
116
|
|
|
116
117
|
- The staging starts as a copy of production, data included: its package,
|
|
117
|
-
inputs, records, apps, task settings and Files
|
|
118
|
-
|
|
119
|
-
stands; `--replace` copies over
|
|
118
|
+
inputs, records, apps, task settings and Files declarations and bindings.
|
|
119
|
+
Its own storage for files starts empty, its values kept (6). `--as-is`
|
|
120
|
+
makes a workspace there the staging as it stands; `--replace` copies over
|
|
121
|
+
one.
|
|
120
122
|
- Production is then protected: its package, apps and Files declarations
|
|
121
123
|
change only by a release from its staging, or a rollback. People go on
|
|
122
124
|
using it: inputs, records, runs and schedules.
|
|
@@ -190,21 +192,27 @@ e3-cloud release URL staging production --plan
|
|
|
190
192
|
### 6. Files
|
|
191
193
|
|
|
192
194
|
```bash
|
|
193
|
-
e3-cloud files deploy URL staging src/files.ts
|
|
194
|
-
e3-cloud files bind URL staging upload s3://bucket/acme/staging/
|
|
195
|
-
e3-cloud files sync URL staging
|
|
195
|
+
e3-cloud files deploy URL staging src/files.ts # its sources read staging's own storage
|
|
196
196
|
e3-cloud files push URL staging ./data/prices.beast2 ./data/customers.beast2 # from disk: matched by name (or --as), put, synced
|
|
197
197
|
e3-cloud files push URL staging dataset:dev.customers --from ./repo # a local workspace's dataset, as the file of its value
|
|
198
|
+
e3-cloud files bind URL staging upload ../production/upload/ # or read production's files
|
|
198
199
|
e3-cloud files sync URL staging --target record:customers --reset customers # named targets; a seeded record's file as its state again
|
|
199
200
|
e3 dataflow run URL staging
|
|
200
201
|
```
|
|
201
202
|
|
|
203
|
+
- A deployed workspace keeps its sources' files in storage of its own, a
|
|
204
|
+
folder each (`upload/`), which `files push` puts them in with your e3 login
|
|
205
|
+
alone: nothing to bind, and no command names a bucket. `files bind` points
|
|
206
|
+
a source elsewhere: a path relative to the workspace (`../production/upload/`
|
|
207
|
+
is production's folder, and a push through it puts there), or
|
|
208
|
+
`s3://bucket/prefix/`, a bucket of your own the deployment grants.
|
|
202
209
|
- The declarations (which inputs files fill) travel with releases; where
|
|
203
210
|
each source's files are (its binding) is each workspace's own. A staging
|
|
204
|
-
copied from production
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
211
|
+
copied from production keeps production's values, its own storage empty:
|
|
212
|
+
a push adds to them, and its first sync wipes nothing. Bind its sources to
|
|
213
|
+
`../production/<source>/` to rehearse on production's files.
|
|
214
|
+
- A source production hasn't bound reads production's own storage: push its
|
|
215
|
+
files there after the release, or bind it to a bucket of your own before.
|
|
208
216
|
- A release syncs nothing: after it, `files sync URL production`, then run
|
|
209
217
|
production. Production refuses `files deploy` (9 for the exception).
|
|
210
218
|
Declaring and decoding the files: the e3-cloud-files skill.
|
|
@@ -376,7 +384,7 @@ one of its own.
|
|
|
376
384
|
- A protected workspace refuses e3's `workspace deploy`, an app deploy or
|
|
377
385
|
removal, a Files deploy, a copy onto it, and its removal or its staging's.
|
|
378
386
|
Its data goes on: inputs, records, runs and schedules, and its Files
|
|
379
|
-
bindings and syncs.
|
|
387
|
+
bindings, pushes and syncs.
|
|
380
388
|
|
|
381
389
|
## Releases
|
|
382
390
|
|
|
@@ -401,21 +409,27 @@ and runs next by hand or on its schedule. Owners and admins release.
|
|
|
401
409
|
|
|
402
410
|
## Files
|
|
403
411
|
|
|
404
|
-
A solution declares in its code which of its inputs come from files
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
a repository URL:
|
|
412
|
+
A solution declares in its code which of its inputs come from files
|
|
413
|
+
(`@elaraai/e3-cloud-files`, its own skill). The CLI deploys the declarations,
|
|
414
|
+
pushes files, binds a source elsewhere and syncs, given a local repository's
|
|
415
|
+
path or a repository URL:
|
|
408
416
|
|
|
409
417
|
- `files deploy <repo> <ws> <source>` runs the source (its default export is
|
|
410
418
|
`Files.manifest(...)`) and checks the declarations against the package the
|
|
411
419
|
workspace runs; `--dry-run` keeps nothing. A protected workspace refuses
|
|
412
420
|
it: its declarations come by a release (`--force`, an admin's override).
|
|
413
|
-
- `files bind <repo> <ws> <source>
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
421
|
+
- `files bind <repo> <ws> <source> [location]` says where a source's files
|
|
422
|
+
are when not in the workspace's own storage under its name (a deployed
|
|
423
|
+
workspace's default): a path relative to the workspace (`inbound/`, `./`,
|
|
424
|
+
`../production/upload/`), shown as `own storage <path>`; or
|
|
425
|
+
`s3://bucket/prefix/`, a bucket of your own (`--region` when the bucket's
|
|
426
|
+
isn't yours), as every binding of a local repository is, which has no
|
|
427
|
+
storage of its own. No location binds it to its own folder again. Locally
|
|
428
|
+
only: `--profile` (the profile to read with), `--latest n` (each folder's
|
|
429
|
+
n greatest keys). A workspace's bindings are its own: a release never
|
|
417
430
|
changes them, and a source may be bound before a release brings the
|
|
418
|
-
declarations that read it.
|
|
431
|
+
declarations that read it. Bound elsewhere, the targets that took files
|
|
432
|
+
where it was are taken again whole by the next sync, which it names.
|
|
419
433
|
- `files sync <repo> <ws>` reads only files whose ETag or size changed and
|
|
420
434
|
writes only inputs whose files changed; `--target`, `--reset`, `--dry-run`. It doesn't
|
|
421
435
|
run the dataflow: `e3 dataflow run` does, and the sync prints the command.
|
|
@@ -429,7 +443,9 @@ a repository URL:
|
|
|
429
443
|
…); `--reset`, `--dry-run`, `--profile` (a bucket the platform doesn't
|
|
430
444
|
write). A seed's or feed's history names the file, and the dataset it was
|
|
431
445
|
pushed from.
|
|
432
|
-
- `files status <repo> <ws>`: the declarations,
|
|
446
|
+
- `files status <repo> <ws>`: the declarations, where each source's files
|
|
447
|
+
are (`own storage upload/`, or a bucket's URL), what each mapping holds,
|
|
448
|
+
the last sync.
|
|
433
449
|
|
|
434
450
|
## Traps
|
|
435
451
|
|
|
@@ -441,8 +457,9 @@ a repository URL:
|
|
|
441
457
|
- Sign in with `e3 auth login <server>`: there is no `e3 login`. A run is
|
|
442
458
|
`e3 dataflow run <repo> <ws>`: there is no `e3 start`.
|
|
443
459
|
- A release or a sync runs nothing: run the dataflow after either.
|
|
444
|
-
- A
|
|
445
|
-
|
|
460
|
+
- A source a release's Files declarations read that the target hasn't bound
|
|
461
|
+
reads the target's own storage: bind it in the target first when its files
|
|
462
|
+
are in a bucket of your own, or push them to the target after.
|
|
446
463
|
- `secrets set` reads the value from stdin unless `--value` is given; the
|
|
447
464
|
next run reads the new value.
|
|
448
465
|
- The version is e3 cloud's own, not e3's: use the CLI of the e3 cloud
|
|
@@ -74,19 +74,28 @@ try {
|
|
|
74
74
|
## Files
|
|
75
75
|
|
|
76
76
|
- `getFiles(url, repo, ws, options)` answers a workspace's Files: its
|
|
77
|
-
declarations (`manifest`), where each source
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
declarations (`manifest`), where each declared source's files are
|
|
78
|
+
(`bindings`: a path of the workspace's own storage, relative to it, which
|
|
79
|
+
a source bound to nothing reads as `<source>/`; or a bucket's URL), the
|
|
80
|
+
last sync, a sync under way, the buckets the deployment grants besides
|
|
81
|
+
(`granted`), and `status` — per mapping, what it holds and whether its
|
|
82
|
+
record is `pending` (seeded from a file, its file not yet arrived). No
|
|
83
|
+
answer names the bucket that holds own storage.
|
|
81
84
|
- `deployFiles(url, repo, ws, manifest, options `{ dryRun?, force? }`)` deploys declarations
|
|
82
85
|
(`Files.manifest(...)` from `@elaraai/e3-cloud-files`): `deployed` with
|
|
83
86
|
the targets the next sync takes again whole (`retaken`), `checked` for a
|
|
84
87
|
dry run, or `refused` with every problem. A workspace with a staging
|
|
85
88
|
refuses it (`workspace_protected`) unless an admin forces it.
|
|
86
89
|
- `bindFilesSource(url, repo, ws, binding, options)` says where a source's
|
|
87
|
-
files are
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
files are when not in the workspace's own storage under its name: `url` a
|
|
91
|
+
path relative to the workspace (`inbound/`, `./`, `../production/upload/`;
|
|
92
|
+
`ownStoragePathProblem` from `@elaraai/e3-cloud-types` says why one isn't),
|
|
93
|
+
or `s3://bucket/prefix/`, a bucket the deployment grants. It answers
|
|
94
|
+
`bound` with the targets that took files where it was, which the next
|
|
95
|
+
sync takes again whole, or `refused` (a malformed path, a bucket the
|
|
96
|
+
deployment does not let the repository bind). Bindings are each
|
|
97
|
+
workspace's own, and relative to it: in a copy, a path names the copy's
|
|
98
|
+
own storage, which starts empty. A release never changes them.
|
|
90
99
|
- `startFilesSync(url, repo, ws, { dryRun?, targets?, reset? }, options)`
|
|
91
100
|
answers `started` with the sync's id, or `running` with the one under way;
|
|
92
101
|
`filesSyncWait(url, repo, ws, id, options, { deadlineMs?, refresh?, signal? })`
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: e3-cloud-files
|
|
3
|
-
description: "Files for e3 cloud — a solution's inputs that come from files
|
|
3
|
+
description: "Files for e3 cloud — a solution's inputs that come from files, declared in its code and synced when asked, the same on a local repository and a deployed workspace: in a deployed workspace's own storage (named by a path relative to the workspace, never a bucket) or a bucket of your own (s3://bucket/prefix/). Use when: (1) Declaring which inputs a bucket's files fill with Files.source(name) and its file, latest, folder and listing, then Files.manifest([...]) as a module's default export, (2) Decoding what arrives in a task: a Blob (file, latest), a Dict of path to Blob (folder) or a listing of sizes, times and ETags, as East's JSON, CSV or text, (3) Deploying the declarations to a workspace with e3-cloud files deploy, each source reading the workspace's own storage until e3-cloud files bind says otherwise (a path such as upload/ or ../production/upload/, or s3://bucket/prefix/ for a bucket of your own, which a local repository… See the detailed scope below."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
## Detailed skill scope
|
|
7
7
|
|
|
8
|
-
Files for e3 cloud — a solution's inputs that come from files
|
|
8
|
+
Files for e3 cloud — a solution's inputs that come from files, declared in its code and synced when asked, the same on a local repository and a deployed workspace: in a deployed workspace's own storage (named by a path relative to the workspace, never a bucket) or a bucket of your own (s3://bucket/prefix/). Use when: (1) Declaring which inputs a bucket's files fill with Files.source(name) and its file, latest, folder and listing, then Files.manifest([...]) as a module's default export, (2) Decoding what arrives in a task: a Blob (file, latest), a Dict of path to Blob (folder) or a listing of sizes, times and ETags, as East's JSON, CSV or text, (3) Deploying the declarations to a workspace with e3-cloud files deploy, each source reading the workspace's own storage until e3-cloud files bind says otherwise (a path such as upload/ or ../production/upload/, or s3://bucket/prefix/ for a bucket of your own, which a local repository always needs), (4) Syncing what changed with e3-cloud files sync, locally with your own cloud profile (--profile, --latest), then running the dataflow with e3 dataflow run, (5) Reading a refusal: declarations that don't fit the package (FilesDeclarationError, a deploy's problems), an unbound source, a file over its limit, a protected workspace (workspace_protected), (6) Moving declarations to production through staging: a release carries them with the package and apps, a rollback restores them, a copy keeps its values and starts with empty storage of its own, and production's bindings stay its own, (7) Reading Files' East types (FilesManifestType, FilesBindingType, FilesSyncReportType — @elaraai/e3-cloud-types), (8) Taking a beast2 file in as an input of its type with no task in between: file or latest with a type, a collection of any size, (9) Feeding a record's mutation from files with source.mutation(def, spec), def one its record declares (priceBook.mutations.load_prices), each new file applied once, (10) Pushing files from disk into a source and taking them in with e3-cloud files push, matched by name or --as, dry-run, unchanged, or a workspace's dataset (a task's output, an input, a record) as the file of its value with dataset:workspace.name and --from, local or remote at either end, (11) Seeding a record from a file with source.record(name, type, spec, parts): e3.record with no initial value, declared whole with its indexes, mutations and migrations, pending until its file, its tasks waiting meanwhile, (12) Syncing or resetting particular targets with --target and --reset.
|
|
9
9
|
|
|
10
10
|
# e3-cloud-files — files as e3 inputs
|
|
11
11
|
|
|
12
|
-
A solution says in its code which of its inputs come from files
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
A solution says in its code which of its inputs come from files, and which
|
|
13
|
+
records are fed from them. A deployed workspace keeps those files in storage
|
|
14
|
+
of its own, a folder for each source (`upload/`), which `e3-cloud files push`
|
|
15
|
+
puts them in with an e3 login alone: nothing to bind, no bucket to name. A
|
|
16
|
+
workspace may bind a source elsewhere: another folder of its repository's
|
|
17
|
+
storage, by a path relative to the workspace (`../production/upload/`), or a
|
|
18
|
+
bucket of your own (`s3://bucket/prefix/`), which a local repository, having
|
|
19
|
+
no storage of its own, always names. `e3-cloud files sync` brings in what
|
|
20
|
+
changed: on a local repository with your own cloud credentials, in the cloud
|
|
21
|
+
with the platform's read-only role. A file arrives as it is, a `Blob`, and a task
|
|
17
22
|
decodes it; or, named with a type, a beast2 file arrives as the value it
|
|
18
23
|
holds, with no task in between. `e3-cloud files push` puts files from disk,
|
|
19
24
|
or a workspace's datasets as the files of their values, into a source and
|
|
@@ -58,11 +63,15 @@ export default e3.package('orders', '1.0.0', rateTable);
|
|
|
58
63
|
```
|
|
59
64
|
|
|
60
65
|
```bash
|
|
61
|
-
|
|
62
|
-
e3
|
|
66
|
+
# A deployed workspace: upload reads its own storage, upload/, so nothing to bind
|
|
67
|
+
e3 workspace deploy $REPO dev --from-source src/package.ts # REPO=https://e3.example.com/repos/acme
|
|
68
|
+
e3-cloud files deploy $REPO dev src/files.ts # checked against the package dev runs
|
|
69
|
+
e3-cloud files push $REPO dev rates.json # into dev's own storage, then taken in
|
|
70
|
+
e3 dataflow run $REPO dev # the dataflow runs on it
|
|
71
|
+
|
|
72
|
+
# A local repository: no storage of its own, so bind each source to a bucket
|
|
63
73
|
e3-cloud files bind ./repo dev upload s3://bucket/prefix/ --profile p # where upload's files are, for dev
|
|
64
74
|
e3-cloud files sync ./repo dev # what changed comes in
|
|
65
|
-
e3 dataflow run ./repo dev # the dataflow runs on it
|
|
66
75
|
```
|
|
67
76
|
|
|
68
77
|
## Decision Tree
|
|
@@ -70,7 +79,7 @@ e3 dataflow run ./repo dev # the da
|
|
|
70
79
|
```
|
|
71
80
|
What do you need?
|
|
72
81
|
├─ Declare which inputs a bucket's files fill (src/files.ts)
|
|
73
|
-
│ ├─ A place files come from → Files.source(name) —
|
|
82
|
+
│ ├─ A place files come from → Files.source(name) — a deployed workspace's own storage, <name>/, unless bound elsewhere
|
|
74
83
|
│ ├─ One known file → source.file(input, { key }) input: Blob
|
|
75
84
|
│ ├─ A beast2 file as its value → source.file(input, { key, type: ArrayType(Row) }) input: Array<Row>, no task between
|
|
76
85
|
│ ├─ The newest of a folder → source.latest(input, { prefix, include? }) input: Blob, the greatest key
|
|
@@ -90,8 +99,12 @@ What do you need?
|
|
|
90
99
|
│ └─ Choose files by name or time → a listing: rows.filter(($, row) => row.modified.greaterThan(since))
|
|
91
100
|
├─ Bring the files into a workspace (the e3-cloud CLI; <repo> a local path or a repository URL)
|
|
92
101
|
│ ├─ Deploy the declarations → e3-cloud files deploy <repo> <ws> src/files.ts [--dry-run]
|
|
93
|
-
│ ├─
|
|
94
|
-
│
|
|
102
|
+
│ ├─ Where a source's files are → a deployed workspace's own storage, <source>/: push puts them there, nothing to bind
|
|
103
|
+
│ ├─ Bind a source elsewhere → e3-cloud files bind <repo> <ws> <source> [path | s3://bucket/prefix/] [--region r]
|
|
104
|
+
│ │ ├─ Another folder of its own → a path relative to the workspace: inbound/, ./ (its folder); none: <source>/ again
|
|
105
|
+
│ │ ├─ Another workspace's files → ../<workspace>/<folder>/ (a staging reading production's: ../production/upload/)
|
|
106
|
+
│ │ ├─ A bucket of your own → s3://bucket/prefix/ (one the deployment's configuration grants the repository)
|
|
107
|
+
│ │ └─ A local repository → s3://bucket/prefix/ always (no storage of its own), --profile <aws profile>, --latest <n>
|
|
95
108
|
│ ├─ Bring in what changed → e3-cloud files sync <repo> <ws> [--target t…] [--reset record…] [--dry-run]
|
|
96
109
|
│ ├─ Put files from disk → e3-cloud files push <repo> <ws> <file|dir>… [--as target] [--dry-run] [--profile p]
|
|
97
110
|
│ ├─ Push a workspace's dataset → e3-cloud files push <repo> <ws> dataset:<ws>.<name>… [--from <repo>] (a task's output, an input, a record)
|
|
@@ -99,12 +112,12 @@ What do you need?
|
|
|
99
112
|
│ └─ Run the dataflow on it → e3 dataflow run <repo> <ws> (a sync doesn't run it)
|
|
100
113
|
├─ Production: a workspace with a staging (a deployed repository's URL)
|
|
101
114
|
│ ├─ Change its declarations → files deploy to the staging, then e3-cloud release <url> <staging> <production>
|
|
102
|
-
│ ├─ A source it doesn't bind
|
|
115
|
+
│ ├─ A source it doesn't bind → reads production's own storage, <source>/ (push there), unless bound elsewhere
|
|
103
116
|
│ ├─ See what a release does → e3-cloud release … --plan (each input added, changed, unchanged, removed)
|
|
104
117
|
│ ├─ After a release → e3-cloud files sync <url> <production>, then e3 dataflow run
|
|
105
118
|
│ ├─ Undo a release → e3-cloud rollback <url> <production> (declarations go back with the package)
|
|
106
119
|
│ ├─ An emergency change → e3-cloud files deploy … --force (an admin's; history records it; the next release undoes it)
|
|
107
|
-
│ └─ Rehearse on production's files → e3-cloud staging add … (a copy:
|
|
120
|
+
│ └─ Rehearse on production's files → e3-cloud staging add … (a copy: values kept, own storage empty), then bind ../production/upload/
|
|
108
121
|
├─ Keep synced inputs across a package deploy → e3 workspace deploy … --inputs keep-edited
|
|
109
122
|
└─ Drive it from code
|
|
110
123
|
├─ The checks → @elaraai/e3-cloud-files/check: checkFiles, checkBindings, readPackageSurface
|
|
@@ -119,7 +132,7 @@ What do you need?
|
|
|
119
132
|
// src/files.ts
|
|
120
133
|
import { Files } from '@elaraai/e3-cloud-files';
|
|
121
134
|
|
|
122
|
-
export const upload = Files.source('upload'); //
|
|
135
|
+
export const upload = Files.source('upload'); // a deployed workspace's own storage, upload/, unless bound elsewhere
|
|
123
136
|
|
|
124
137
|
export const rates = upload.file('rates', { key: 'rates.json' }); // input: Blob
|
|
125
138
|
export const stock = upload.latest('stock', { prefix: 'stock/', include: '*.json' }); // input: Blob, the greatest key
|
|
@@ -153,8 +166,9 @@ export default Files.manifest([upload]); // what e3-cloud files deploy dep
|
|
|
153
166
|
bytes' hash, so the same bytes under another name are applied no more; a
|
|
154
167
|
file the mutation refuses holds the ones after it until it changes; a busy
|
|
155
168
|
record leaves the file to the next sync.
|
|
156
|
-
- Keys and prefixes are relative to where
|
|
157
|
-
|
|
169
|
+
- Keys and prefixes are relative to where the source's files are (a
|
|
170
|
+
deployed workspace's own `upload/`, or where it binds the source); a prefix
|
|
171
|
+
is `''` or ends with `/`.
|
|
158
172
|
- `include` is a glob over the path under the prefix: `*` within one folder,
|
|
159
173
|
`**` across folders, `?` one character, `[a-z]` one of a set (`[!_]` any
|
|
160
174
|
but), `{csv,json}` any of its alternatives (`*.{csv,json}`).
|
|
@@ -179,7 +193,7 @@ file's path under the prefix, in path order.
|
|
|
179
193
|
|
|
180
194
|
```
|
|
181
195
|
e3-cloud files deploy <repo> <workspace> src/files.ts [--dry-run]
|
|
182
|
-
e3-cloud files bind <repo> <workspace> upload s3://bucket/prefix/ [--region r] [--profile p] [--latest n]
|
|
196
|
+
e3-cloud files bind <repo> <workspace> upload [path | s3://bucket/prefix/] [--region r] [--profile p] [--latest n]
|
|
183
197
|
e3-cloud files sync <repo> <workspace> [--target name|record:r|mutation:r.m ...] [--reset record...] [--dry-run]
|
|
184
198
|
e3-cloud files push <repo> <workspace> <file|dir|dataset:ws.name>... [--from repo] [--as target] [--reset record...] [--dry-run] [--profile p]
|
|
185
199
|
e3-cloud files status <repo> <workspace>
|
|
@@ -188,9 +202,10 @@ e3 dataflow run <repo> <workspace>
|
|
|
188
202
|
|
|
189
203
|
`files push` matches each file to the mapping that takes it (a `file` by its
|
|
190
204
|
name, a `latest` under its prefix, a directory to the `folder`; `--as` names
|
|
191
|
-
one), checks its type and size, puts it where the source
|
|
192
|
-
|
|
193
|
-
file's SHA-256; your own credentials
|
|
205
|
+
one), checks its type and size, puts it where the source's files are (into
|
|
206
|
+
the workspace's own storage by an upload the platform signs, bound to the
|
|
207
|
+
file's SHA-256; into a bucket of your own with your own credentials), then
|
|
208
|
+
syncs what was put. A
|
|
194
209
|
file the source holds already is `unchanged`; refused: `no_mapping`,
|
|
195
210
|
`ambiguous` (name the target with `--as`), `not_included` (the mapping's
|
|
196
211
|
glob leaves it out), `not_latest` (a greater key would shadow it), a typed
|
|
@@ -226,7 +241,11 @@ its state again, whatever mutated it since.
|
|
|
226
241
|
|
|
227
242
|
`<repo>` is a local repository's path or a repository URL. Deploy the package
|
|
228
243
|
first (`e3 workspace deploy`), then the declarations, which are checked
|
|
229
|
-
against it. A
|
|
244
|
+
against it. A deployed workspace's sources read its own storage until bound
|
|
245
|
+
elsewhere, so `files push` works after `files deploy` alone; a local
|
|
246
|
+
repository has none, so bind each source to a bucket before its first sync.
|
|
247
|
+
Own storage is named by its path alone (`own storage upload/`): no command,
|
|
248
|
+
history or answer names the bucket that holds it. A sync reads only files whose ETag or size changed, writes only
|
|
230
249
|
inputs whose files changed, and prints the next command when any did. It
|
|
231
250
|
doesn't run the dataflow: `e3 dataflow run` does. Locally the bytes land only in the
|
|
232
251
|
repository; `--profile` names the profile to read with and `--latest n`
|
|
@@ -270,15 +289,19 @@ keeps only each folder's n greatest keys.
|
|
|
270
289
|
refuses until they're deployed again. A package deploy also resets synced
|
|
271
290
|
inputs unless it keeps them (`--inputs keep-edited`); the next sync takes
|
|
272
291
|
them again whole.
|
|
273
|
-
- A changed declaration
|
|
274
|
-
|
|
292
|
+
- A changed declaration has its targets taken again whole at the next sync;
|
|
293
|
+
so does a source bound elsewhere, for the targets that took files where it
|
|
294
|
+
was (`files bind` names them).
|
|
275
295
|
- A workspace with a staging refuses `files deploy` (`workspace_protected`):
|
|
276
|
-
deploy to the staging and release. A
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
- Bindings never travel with a release; a copy carries them
|
|
280
|
-
|
|
281
|
-
|
|
296
|
+
deploy to the staging and release. A source production hasn't bound reads
|
|
297
|
+
production's own storage, `<source>/`: push its files there, or bind it
|
|
298
|
+
elsewhere. A release syncs nothing: sync production after it.
|
|
299
|
+
- Bindings never travel with a release; a copy carries them, relative to the
|
|
300
|
+
copy. A source in own storage (bound by a path, or to nothing) reads the
|
|
301
|
+
copy's own storage, which starts empty; the copy keeps the values it was
|
|
302
|
+
given, a file put there adds to them or replaces them, and its first sync
|
|
303
|
+
wipes nothing. To rehearse on production's files, bind the staging's source
|
|
304
|
+
to `../production/upload/`; a bucket of your own stays where it was.
|
|
282
305
|
- The version is e3 cloud's own, not e3's: install the release built with the
|
|
283
306
|
e3 the deployment runs, its peers (east, e3) at exactly the versions it
|
|
284
307
|
names, and the `e3-cloud` CLI of the same release.
|