lua-cli 3.14.1 → 3.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/agent.api.service.d.ts +12 -0
- package/dist/api/agent.api.service.d.ts.map +1 -1
- package/dist/api/agent.api.service.js +13 -0
- package/dist/api/agent.api.service.js.map +1 -1
- package/dist/api/http.client.d.ts.map +1 -1
- package/dist/api/http.client.js +31 -4
- package/dist/api/http.client.js.map +1 -1
- package/dist/api/persona.api.service.d.ts +3 -2
- package/dist/api/persona.api.service.d.ts.map +1 -1
- package/dist/api/persona.api.service.js +1 -1
- package/dist/api/persona.api.service.js.map +1 -1
- package/dist/api/postprocessor.api.service.d.ts +3 -0
- package/dist/api/postprocessor.api.service.d.ts.map +1 -1
- package/dist/api/postprocessor.api.service.js.map +1 -1
- package/dist/api/preprocessor.api.service.d.ts +4 -0
- package/dist/api/preprocessor.api.service.d.ts.map +1 -1
- package/dist/api/preprocessor.api.service.js +1 -0
- package/dist/api/preprocessor.api.service.js.map +1 -1
- package/dist/api-exports.d.ts +2 -1
- package/dist/api-exports.d.ts.map +1 -1
- package/dist/api-exports.js.map +1 -1
- package/dist/cli/command-definitions.d.ts.map +1 -1
- package/dist/cli/command-definitions.js +56 -0
- package/dist/cli/command-definitions.js.map +1 -1
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +325 -85
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/persona.d.ts.map +1 -1
- package/dist/commands/persona.js +3 -2
- package/dist/commands/persona.js.map +1 -1
- package/dist/commands/production.d.ts.map +1 -1
- package/dist/commands/production.js +8 -2
- package/dist/commands/production.js.map +1 -1
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +94 -14
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/pushBackup.d.ts +58 -4
- package/dist/commands/pushBackup.d.ts.map +1 -1
- package/dist/commands/pushBackup.js +202 -125
- package/dist/commands/pushBackup.js.map +1 -1
- package/dist/commands/source.d.ts +72 -0
- package/dist/commands/source.d.ts.map +1 -0
- package/dist/commands/source.js +124 -0
- package/dist/commands/source.js.map +1 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +3 -2
- package/dist/commands/sync.js.map +1 -1
- package/dist/compiler/plugins/agent.plugin.d.ts.map +1 -1
- package/dist/compiler/plugins/agent.plugin.js +10 -5
- package/dist/compiler/plugins/agent.plugin.js.map +1 -1
- package/dist/compiler/plugins/postprocessor.plugin.d.ts.map +1 -1
- package/dist/compiler/plugins/postprocessor.plugin.js +8 -2
- package/dist/compiler/plugins/postprocessor.plugin.js.map +1 -1
- package/dist/compiler/plugins/preprocessor.plugin.d.ts.map +1 -1
- package/dist/compiler/plugins/preprocessor.plugin.js +6 -3
- package/dist/compiler/plugins/preprocessor.plugin.js.map +1 -1
- package/dist/compiler/plugins/skill.plugin.d.ts.map +1 -1
- package/dist/compiler/plugins/skill.plugin.js +6 -2
- package/dist/compiler/plugins/skill.plugin.js.map +1 -1
- package/dist/compiler/source-writer.d.ts +13 -8
- package/dist/compiler/source-writer.d.ts.map +1 -1
- package/dist/compiler/source-writer.js +24 -12
- package/dist/compiler/source-writer.js.map +1 -1
- package/dist/compiler/types.d.ts +11 -5
- package/dist/compiler/types.d.ts.map +1 -1
- package/dist/compiler/types.js.map +1 -1
- package/dist/compiler/utils/ast-helpers.d.ts +11 -0
- package/dist/compiler/utils/ast-helpers.d.ts.map +1 -1
- package/dist/compiler/utils/ast-helpers.js +27 -0
- package/dist/compiler/utils/ast-helpers.js.map +1 -1
- package/dist/errors/auth.error.d.ts +19 -1
- package/dist/errors/auth.error.d.ts.map +1 -1
- package/dist/errors/auth.error.js +4 -1
- package/dist/errors/auth.error.js.map +1 -1
- package/dist/instances/user.instance.d.ts +2 -1
- package/dist/instances/user.instance.d.ts.map +1 -1
- package/dist/instances/user.instance.js.map +1 -1
- package/dist/interfaces/agent.d.ts +34 -4
- package/dist/interfaces/agent.d.ts.map +1 -1
- package/dist/interfaces/backup.d.ts +24 -0
- package/dist/interfaces/backup.d.ts.map +1 -1
- package/dist/interfaces/chat.d.ts +3 -22
- package/dist/interfaces/chat.d.ts.map +1 -1
- package/dist/interfaces/init.d.ts +3 -2
- package/dist/interfaces/init.d.ts.map +1 -1
- package/dist/interfaces/persona.d.ts +2 -1
- package/dist/interfaces/persona.d.ts.map +1 -1
- package/dist/interfaces/skills.d.ts +4 -3
- package/dist/interfaces/skills.d.ts.map +1 -1
- package/dist/primitives/agent.handler.d.ts +3 -3
- package/dist/primitives/agent.handler.d.ts.map +1 -1
- package/dist/primitives/agent.handler.js +15 -1
- package/dist/primitives/agent.handler.js.map +1 -1
- package/dist/primitives/postprocessor.handler.d.ts +5 -0
- package/dist/primitives/postprocessor.handler.d.ts.map +1 -1
- package/dist/primitives/postprocessor.handler.js +13 -0
- package/dist/primitives/postprocessor.handler.js.map +1 -1
- package/dist/primitives/preprocessor.handler.d.ts +2 -2
- package/dist/primitives/preprocessor.handler.d.ts.map +1 -1
- package/dist/primitives/preprocessor.handler.js +3 -2
- package/dist/primitives/preprocessor.handler.js.map +1 -1
- package/dist/primitives/skill.handler.d.ts.map +1 -1
- package/dist/primitives/skill.handler.js +8 -2
- package/dist/primitives/skill.handler.js.map +1 -1
- package/dist/services/auth.d.ts.map +1 -1
- package/dist/services/auth.js +4 -1
- package/dist/services/auth.js.map +1 -1
- package/dist/services/sandbox.service.d.ts +2 -1
- package/dist/services/sandbox.service.d.ts.map +1 -1
- package/dist/services/sandbox.service.js.map +1 -1
- package/dist/types/api-contracts.d.ts +4 -3
- package/dist/types/api-contracts.d.ts.map +1 -1
- package/dist/types/skill.d.ts +12 -3
- package/dist/types/skill.d.ts.map +1 -1
- package/dist/types/skill.js +17 -0
- package/dist/types/skill.js.map +1 -1
- package/dist/types/yaml.types.d.ts +6 -0
- package/dist/types/yaml.types.d.ts.map +1 -1
- package/dist/utils/artifact-loader.d.ts +2 -1
- package/dist/utils/artifact-loader.d.ts.map +1 -1
- package/dist/utils/artifact-loader.js.map +1 -1
- package/dist/utils/backup-helpers.d.ts.map +1 -1
- package/dist/utils/backup-helpers.js +12 -2
- package/dist/utils/backup-helpers.js.map +1 -1
- package/dist/utils/build-manifest-from-disk.d.ts +17 -0
- package/dist/utils/build-manifest-from-disk.d.ts.map +1 -0
- package/dist/utils/build-manifest-from-disk.js +111 -0
- package/dist/utils/build-manifest-from-disk.js.map +1 -0
- package/dist/utils/cli.d.ts.map +1 -1
- package/dist/utils/cli.js +34 -1
- package/dist/utils/cli.js.map +1 -1
- package/dist/utils/init-agent.d.ts +2 -1
- package/dist/utils/init-agent.d.ts.map +1 -1
- package/dist/utils/init-agent.js.map +1 -1
- package/dist/utils/init-helpers.d.ts +2 -1
- package/dist/utils/init-helpers.d.ts.map +1 -1
- package/dist/utils/init-helpers.js +3 -1
- package/dist/utils/init-helpers.js.map +1 -1
- package/dist/utils/init-prompts.d.ts +20 -1
- package/dist/utils/init-prompts.d.ts.map +1 -1
- package/dist/utils/init-prompts.js +88 -0
- package/dist/utils/init-prompts.js.map +1 -1
- package/dist/utils/sync-display.d.ts +2 -1
- package/dist/utils/sync-display.d.ts.map +1 -1
- package/dist/utils/sync-display.js +5 -1
- package/dist/utils/sync-display.js.map +1 -1
- package/dist/utils/sync-fetch.d.ts +6 -4
- package/dist/utils/sync-fetch.d.ts.map +1 -1
- package/dist/utils/sync-fetch.js +31 -1
- package/dist/utils/sync-fetch.js.map +1 -1
- package/docs/api/LuaAgent.md +21 -3
- package/docs/api/LuaSkill.md +22 -4
- package/node_modules/@lua/shared-types/dist/index.d.mts +120 -1
- package/node_modules/@lua/shared-types/dist/index.d.ts +120 -1
- package/node_modules/@lua/shared-types/dist/index.js +137 -2
- package/node_modules/@lua/shared-types/dist/index.mjs +129 -1
- package/node_modules/@lua/shared-types/src/chat-history.types.ts +112 -0
- package/node_modules/@lua/shared-types/src/index.ts +2 -0
- package/node_modules/@lua/shared-types/src/persona-text.type.ts +92 -0
- package/package.json +1 -1
- package/template/package.json +1 -1
|
@@ -4,21 +4,75 @@
|
|
|
4
4
|
* Pushes project source files to S3 blob storage for later restoration.
|
|
5
5
|
* Uses content-addressed storage for automatic deduplication.
|
|
6
6
|
*
|
|
7
|
+
* Two entry points:
|
|
8
|
+
* - `pushBackupCommand(opts)`: the user-facing `lua push backup` wrapper.
|
|
9
|
+
* Adds the freshness/server-newer guard, telemetry, and the
|
|
10
|
+
* "skip-when-server-already-has-this-hash" short-circuit. Falls through
|
|
11
|
+
* to `runBackupPush` for the actual upload + manifest save.
|
|
12
|
+
* - `runBackupPush(opts)`: pure programmatic entry. Used by the auto-
|
|
13
|
+
* backup hook in `pushCommand` (Phase 2 task 2.5) — must throw on
|
|
14
|
+
* failure so the surrounding `lua push <X>` exits non-zero.
|
|
15
|
+
*
|
|
7
16
|
* Flow:
|
|
8
|
-
* 1.
|
|
9
|
-
* 2.
|
|
10
|
-
* 3.
|
|
11
|
-
* 4.
|
|
17
|
+
* 1. Build a manifest (fresh-from-disk OR from compiled manifest.json).
|
|
18
|
+
* 2. checkBlobsExist → upload missing blobs.
|
|
19
|
+
* 3. saveManifest (V2 path on server returns activeVersion).
|
|
20
|
+
* 4. Persist `lastHash` + `activeVersion` into lua.skill.yaml.
|
|
21
|
+
*/
|
|
22
|
+
export interface RunBackupPushOptions {
|
|
23
|
+
apiKey: string;
|
|
24
|
+
agentId: string;
|
|
25
|
+
/** Bypass the local up-to-date short-circuit. */
|
|
26
|
+
force: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Build the manifest by walking `projectPath` from disk instead of
|
|
29
|
+
* loading the compiled manifest.json. Required by the auto-backup hook:
|
|
30
|
+
* the hook fires after a primitive push, where the Builder may have
|
|
31
|
+
* written files the most recent compile didn't see.
|
|
32
|
+
*/
|
|
33
|
+
fresh: boolean;
|
|
34
|
+
/** Free-form audit label sent to the server. */
|
|
35
|
+
triggeredBy?: string;
|
|
36
|
+
/** Defaults to `process.cwd()`. */
|
|
37
|
+
projectPath?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface RunBackupPushResult {
|
|
40
|
+
/** Server-assigned manifest version (0 when the server didn't return one). */
|
|
41
|
+
activeVersion: number;
|
|
42
|
+
/** Number of new blobs the CLI had to PUT to S3 (deduped count = total − this). */
|
|
43
|
+
filesUploaded: number;
|
|
44
|
+
/** True when the up-to-date check short-circuited (only possible when `force=false`). */
|
|
45
|
+
skipped: boolean;
|
|
46
|
+
/** Manifest projectHash that was pushed (or matched). Useful for logging. */
|
|
47
|
+
projectHash: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Programmatic push entry. Pure (no console output beyond the optional
|
|
51
|
+
* telemetry events callers may add at their layer), and it THROWS on
|
|
52
|
+
* failure rather than swallowing — call sites in `pushCommand` rely on
|
|
53
|
+
* that to fail the surrounding `lua push <X>` if the auto-backup-push
|
|
54
|
+
* doesn't succeed.
|
|
12
55
|
*/
|
|
56
|
+
export declare function runBackupPush(opts: RunBackupPushOptions): Promise<RunBackupPushResult>;
|
|
13
57
|
/**
|
|
14
58
|
* Push project backup to server using S3 blob storage.
|
|
15
59
|
*
|
|
60
|
+
* This is the user-facing `lua push backup` command. Adds the
|
|
61
|
+
* freshness/server-newer guard, telemetry, and console output around
|
|
62
|
+
* the pure `runBackupPush`. The freshness guard is intentionally NOT in
|
|
63
|
+
* `runBackupPush` because the auto-backup hook should always re-push
|
|
64
|
+
* after a successful primitive push, even if another machine has a
|
|
65
|
+
* newer backup — the primitive push is the source of truth at that
|
|
66
|
+
* point.
|
|
67
|
+
*
|
|
16
68
|
* @param options - Command options
|
|
17
69
|
* @param options.force - Force push even if backup is up-to-date
|
|
18
70
|
* @param options.skipCompile - Skip compilation (assume already compiled)
|
|
71
|
+
* @param options.fresh - Build manifest from disk (Phase 2 --fresh flag)
|
|
19
72
|
*/
|
|
20
73
|
export declare function pushBackupCommand(options?: {
|
|
21
74
|
force?: boolean;
|
|
22
75
|
skipCompile?: boolean;
|
|
76
|
+
fresh?: boolean;
|
|
23
77
|
}): Promise<void>;
|
|
24
78
|
//# sourceMappingURL=pushBackup.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pushBackup.d.ts","sourceRoot":"","sources":["../../src/commands/pushBackup.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"pushBackup.d.ts","sourceRoot":"","sources":["../../src/commands/pushBackup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AA4BH,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,KAAK,EAAE,OAAO,CAAC;IACf;;;;;OAKG;IACH,KAAK,EAAE,OAAO,CAAC;IACf,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,8EAA8E;IAC9E,aAAa,EAAE,MAAM,CAAC;IACtB,mFAAmF;IACnF,aAAa,EAAE,MAAM,CAAC;IACtB,yFAAyF;IACzF,OAAO,EAAE,OAAO,CAAC;IACjB,6EAA6E;IAC7E,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA+H5F;AAqDD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;CACZ,GACL,OAAO,CAAC,IAAI,CAAC,CAwEf"}
|
|
@@ -4,63 +4,224 @@
|
|
|
4
4
|
* Pushes project source files to S3 blob storage for later restoration.
|
|
5
5
|
* Uses content-addressed storage for automatic deduplication.
|
|
6
6
|
*
|
|
7
|
+
* Two entry points:
|
|
8
|
+
* - `pushBackupCommand(opts)`: the user-facing `lua push backup` wrapper.
|
|
9
|
+
* Adds the freshness/server-newer guard, telemetry, and the
|
|
10
|
+
* "skip-when-server-already-has-this-hash" short-circuit. Falls through
|
|
11
|
+
* to `runBackupPush` for the actual upload + manifest save.
|
|
12
|
+
* - `runBackupPush(opts)`: pure programmatic entry. Used by the auto-
|
|
13
|
+
* backup hook in `pushCommand` (Phase 2 task 2.5) — must throw on
|
|
14
|
+
* failure so the surrounding `lua push <X>` exits non-zero.
|
|
15
|
+
*
|
|
7
16
|
* Flow:
|
|
8
|
-
* 1.
|
|
9
|
-
* 2.
|
|
10
|
-
* 3.
|
|
11
|
-
* 4.
|
|
17
|
+
* 1. Build a manifest (fresh-from-disk OR from compiled manifest.json).
|
|
18
|
+
* 2. checkBlobsExist → upload missing blobs.
|
|
19
|
+
* 3. saveManifest (V2 path on server returns activeVersion).
|
|
20
|
+
* 4. Persist `lastHash` + `activeVersion` into lua.skill.yaml.
|
|
12
21
|
*/
|
|
13
22
|
import { withErrorHandling, writeProgress, writeSuccess, writeInfo, writeError } from '../utils/cli.js';
|
|
14
23
|
import { requireAuthOrExit } from '../utils/command-utils.js';
|
|
15
24
|
import { readYamlConfig, writeYamlConfig } from '../utils/files.js';
|
|
16
25
|
import { hasCompilationOutput, loadManifest } from '../utils/artifact-loader.js';
|
|
17
26
|
import { calculateProjectHash, prepareFileRefs, uploadBlobsToS3, getAllFileHashes, createBackupTracking, isBackupOutOfSync, writeBackupManifestCache, checkServerBackupNewerThanLocal, reconcileManifestWithDisk, } from '../utils/backup-helpers.js';
|
|
27
|
+
import { buildManifestFromDisk } from '../utils/build-manifest-from-disk.js';
|
|
18
28
|
import BackupApi from '../api/backup.api.service.js';
|
|
19
29
|
import { BASE_URLS } from '../config/constants.js';
|
|
20
30
|
import { compileCommand } from './compile.js';
|
|
21
31
|
import { trackEvent } from '../services/analytics.js';
|
|
32
|
+
/**
|
|
33
|
+
* Programmatic push entry. Pure (no console output beyond the optional
|
|
34
|
+
* telemetry events callers may add at their layer), and it THROWS on
|
|
35
|
+
* failure rather than swallowing — call sites in `pushCommand` rely on
|
|
36
|
+
* that to fail the surrounding `lua push <X>` if the auto-backup-push
|
|
37
|
+
* doesn't succeed.
|
|
38
|
+
*/
|
|
39
|
+
export async function runBackupPush(opts) {
|
|
40
|
+
const projectPath = opts.projectPath ?? process.cwd();
|
|
41
|
+
const config = readYamlConfig();
|
|
42
|
+
if (!config) {
|
|
43
|
+
throw new Error('No lua.skill.yaml found. Run this from a skill directory.');
|
|
44
|
+
}
|
|
45
|
+
// Build the manifest two ways depending on the caller's needs.
|
|
46
|
+
//
|
|
47
|
+
// - fresh=true: walk projectPath directly. The auto-backup hook needs
|
|
48
|
+
// this because the Builder writes files between compiles; relying
|
|
49
|
+
// on `dist-v2/manifest.json` would silently miss them.
|
|
50
|
+
// - fresh=false: keep the existing flow that hashes whatever the most
|
|
51
|
+
// recent compile produced. Used by `lua push backup` (which runs
|
|
52
|
+
// compile first) so we don't pay the cost of a second disk walk.
|
|
53
|
+
let manifestProjectHash;
|
|
54
|
+
let fileRefs;
|
|
55
|
+
let totalSize = 0;
|
|
56
|
+
if (opts.fresh) {
|
|
57
|
+
const fresh = buildManifestFromDisk(projectPath, opts.agentId);
|
|
58
|
+
manifestProjectHash = fresh.projectHash;
|
|
59
|
+
fileRefs = fresh.files;
|
|
60
|
+
totalSize = fresh.totalSize;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
if (!hasCompilationOutput(projectPath)) {
|
|
64
|
+
throw new Error('No compilation output found. Run "lua compile" first.');
|
|
65
|
+
}
|
|
66
|
+
// See pushBackupCommand notes: yaml-sync runs after manifest.json is
|
|
67
|
+
// written, so the manifest hash for `lua.skill.yaml` is one bump
|
|
68
|
+
// behind disk. Reconcile rewrites blob + entry under the fresh hash.
|
|
69
|
+
reconcileManifestWithDisk(projectPath);
|
|
70
|
+
const manifest = loadManifest(projectPath);
|
|
71
|
+
manifestProjectHash = calculateProjectHash(manifest.projectFiles);
|
|
72
|
+
fileRefs = prepareFileRefs(projectPath);
|
|
73
|
+
}
|
|
74
|
+
// Local up-to-date short-circuit. `force` bypasses this — used by both
|
|
75
|
+
// `lua push backup --force` and the auto-backup hook (which wants to
|
|
76
|
+
// re-push whenever a primitive push happened, even if files didn't
|
|
77
|
+
// change).
|
|
78
|
+
if (!opts.force && !isBackupOutOfSync(manifestProjectHash, config.backup)) {
|
|
79
|
+
return {
|
|
80
|
+
activeVersion: config.backup?.activeVersion ?? 0,
|
|
81
|
+
filesUploaded: 0,
|
|
82
|
+
skipped: true,
|
|
83
|
+
projectHash: manifestProjectHash,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
const backupApi = new BackupApi(BASE_URLS.API, opts.apiKey, opts.agentId);
|
|
87
|
+
// Phase 1: upload missing blobs.
|
|
88
|
+
const allHashes = opts.fresh ? [...new Set(fileRefs.map((f) => f.hash))] : getAllFileHashes(projectPath);
|
|
89
|
+
const checkResponse = await backupApi.checkBlobsExist(allHashes);
|
|
90
|
+
if (!checkResponse.success || !checkResponse.data) {
|
|
91
|
+
throw new Error(checkResponse.error?.message ?? 'Failed to check blob existence');
|
|
92
|
+
}
|
|
93
|
+
const { missing } = checkResponse.data;
|
|
94
|
+
let filesUploaded = 0;
|
|
95
|
+
if (missing.length > 0) {
|
|
96
|
+
const urlsResponse = await backupApi.getBlobUploadUrls(missing);
|
|
97
|
+
if (!urlsResponse.success || !urlsResponse.data) {
|
|
98
|
+
throw new Error(urlsResponse.error?.message ?? 'Failed to get upload URLs');
|
|
99
|
+
}
|
|
100
|
+
if (opts.fresh) {
|
|
101
|
+
// Fresh manifest mode: read blobs directly from disk by relativePath
|
|
102
|
+
// (the file-on-disk IS the source of truth, no sources/<hash>.<ext>
|
|
103
|
+
// sidecar exists). Compresses + PUTs in parallel, mirroring
|
|
104
|
+
// uploadBlobsToS3 but without the manifest dependency.
|
|
105
|
+
await uploadBlobsFromDisk(urlsResponse.data.urls, fileRefs, projectPath);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
const manifest = loadManifest(projectPath);
|
|
109
|
+
await uploadBlobsToS3(urlsResponse.data.urls, manifest, projectPath);
|
|
110
|
+
}
|
|
111
|
+
filesUploaded = missing.length;
|
|
112
|
+
}
|
|
113
|
+
// Phase 2: save the manifest. The V2 server path returns activeVersion.
|
|
114
|
+
const saveResponse = await backupApi.saveManifest({
|
|
115
|
+
projectHash: manifestProjectHash,
|
|
116
|
+
files: fileRefs,
|
|
117
|
+
version: config.skills?.[0]?.version,
|
|
118
|
+
orgId: config.agent?.orgId ?? '',
|
|
119
|
+
createdBy: 'cli',
|
|
120
|
+
triggeredBy: opts.triggeredBy,
|
|
121
|
+
});
|
|
122
|
+
if (!saveResponse.success || !saveResponse.data) {
|
|
123
|
+
throw new Error(saveResponse.error?.message ?? 'Failed to save manifest');
|
|
124
|
+
}
|
|
125
|
+
// Persist tracking. We always update lastHash/lastPushedAt; we update
|
|
126
|
+
// activeVersion only when the server returned one (V2 path) so V1
|
|
127
|
+
// fallback writes don't blow away an existing value with `undefined`.
|
|
128
|
+
const tracking = createBackupTracking(manifestProjectHash);
|
|
129
|
+
config.backup = {
|
|
130
|
+
...(config.backup ?? {}),
|
|
131
|
+
...tracking,
|
|
132
|
+
};
|
|
133
|
+
if (typeof saveResponse.data.activeVersion === 'number') {
|
|
134
|
+
config.backup.activeVersion = saveResponse.data.activeVersion;
|
|
135
|
+
}
|
|
136
|
+
writeYamlConfig(config);
|
|
137
|
+
// Cache for `lua sync --pull` conflict guard. Best-effort.
|
|
138
|
+
writeBackupManifestCache({
|
|
139
|
+
lastHash: manifestProjectHash,
|
|
140
|
+
lastPushedAt: new Date().toISOString(),
|
|
141
|
+
files: fileRefs.filter((f) => !f.external).map((f) => ({ relativePath: f.relativePath, hash: f.hash })),
|
|
142
|
+
}, projectPath);
|
|
143
|
+
// totalSize unused beyond the disk-mode local computation, but useful
|
|
144
|
+
// if/when callers want to surface upload-size telemetry.
|
|
145
|
+
void totalSize;
|
|
146
|
+
return {
|
|
147
|
+
activeVersion: saveResponse.data.activeVersion ?? 0,
|
|
148
|
+
filesUploaded,
|
|
149
|
+
skipped: false,
|
|
150
|
+
projectHash: manifestProjectHash,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Upload a set of file refs to S3 by reading them straight from disk.
|
|
155
|
+
* Mirrors `uploadBlobsToS3` but doesn't depend on `dist-v2/sources/<hash>`
|
|
156
|
+
* being present (the fresh-from-disk path doesn't compile first).
|
|
157
|
+
*/
|
|
158
|
+
async function uploadBlobsFromDisk(uploadUrls, files, projectPath, concurrency = 10) {
|
|
159
|
+
const fs = await import('fs');
|
|
160
|
+
const path = await import('path');
|
|
161
|
+
const zlib = await import('zlib');
|
|
162
|
+
const hashToPath = new Map();
|
|
163
|
+
for (const f of files) {
|
|
164
|
+
if (!hashToPath.has(f.hash))
|
|
165
|
+
hashToPath.set(f.hash, f.relativePath);
|
|
166
|
+
}
|
|
167
|
+
const hashes = Object.keys(uploadUrls);
|
|
168
|
+
for (let i = 0; i < hashes.length; i += concurrency) {
|
|
169
|
+
const batch = hashes.slice(i, i + concurrency);
|
|
170
|
+
await Promise.all(batch.map(async (hash) => {
|
|
171
|
+
const rel = hashToPath.get(hash);
|
|
172
|
+
if (!rel) {
|
|
173
|
+
throw new Error(`No file found for hash: ${hash}`);
|
|
174
|
+
}
|
|
175
|
+
const abs = path.join(projectPath, rel);
|
|
176
|
+
const content = fs.readFileSync(abs);
|
|
177
|
+
const compressed = zlib.gzipSync(content);
|
|
178
|
+
const response = await fetch(uploadUrls[hash], {
|
|
179
|
+
method: 'PUT',
|
|
180
|
+
body: compressed,
|
|
181
|
+
headers: { 'Content-Type': 'application/octet-stream' },
|
|
182
|
+
});
|
|
183
|
+
if (!response.ok) {
|
|
184
|
+
throw new Error(`S3 upload failed for ${hash.substring(0, 12)}...: ${response.status} ${response.statusText}`);
|
|
185
|
+
}
|
|
186
|
+
}));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
22
189
|
// =============================================================================
|
|
23
|
-
//
|
|
190
|
+
// USER-FACING WRAPPER: pushBackupCommand
|
|
24
191
|
// =============================================================================
|
|
25
192
|
/**
|
|
26
193
|
* Push project backup to server using S3 blob storage.
|
|
27
194
|
*
|
|
195
|
+
* This is the user-facing `lua push backup` command. Adds the
|
|
196
|
+
* freshness/server-newer guard, telemetry, and console output around
|
|
197
|
+
* the pure `runBackupPush`. The freshness guard is intentionally NOT in
|
|
198
|
+
* `runBackupPush` because the auto-backup hook should always re-push
|
|
199
|
+
* after a successful primitive push, even if another machine has a
|
|
200
|
+
* newer backup — the primitive push is the source of truth at that
|
|
201
|
+
* point.
|
|
202
|
+
*
|
|
28
203
|
* @param options - Command options
|
|
29
204
|
* @param options.force - Force push even if backup is up-to-date
|
|
30
205
|
* @param options.skipCompile - Skip compilation (assume already compiled)
|
|
206
|
+
* @param options.fresh - Build manifest from disk (Phase 2 --fresh flag)
|
|
31
207
|
*/
|
|
32
208
|
export async function pushBackupCommand(options = {}) {
|
|
33
209
|
await withErrorHandling(async () => {
|
|
34
|
-
// Authenticate
|
|
35
210
|
const apiKey = await requireAuthOrExit();
|
|
36
|
-
// Read config
|
|
37
211
|
const config = readYamlConfig();
|
|
38
212
|
if (!config?.agent?.agentId) {
|
|
39
213
|
throw new Error('Agent not configured. Run "lua init" first to set up your agent.');
|
|
40
214
|
}
|
|
41
215
|
const agentId = config.agent.agentId;
|
|
42
|
-
// Refuse to overwrite a newer server backup
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
// clobber their backup and cost them a pull — the exact data-loss
|
|
46
|
-
// pattern we saw during the sync --pull investigation.
|
|
47
|
-
//
|
|
48
|
-
// Bypassed by `--force`: this is the same flag semantics used by
|
|
49
|
-
// `lua sync --pull --force` and by `lua push all --force`, which
|
|
50
|
-
// passes `force: true` into here. The freshness check is a default
|
|
51
|
-
// safety net, not a hard wall.
|
|
216
|
+
// Refuse to overwrite a newer server backup pushed by another machine.
|
|
217
|
+
// Bypassed by --force. See the original commentary preserved in
|
|
218
|
+
// checkServerBackupNewerThanLocal for the data-loss case this guards.
|
|
52
219
|
if (!options.force) {
|
|
53
220
|
const freshness = await checkServerBackupNewerThanLocal(apiKey, agentId);
|
|
54
221
|
if (freshness.checkFailed) {
|
|
55
222
|
writeInfo('⚠️ Could not verify server backup freshness — proceeding without check.');
|
|
56
223
|
}
|
|
57
224
|
else if (freshness.serverNewer) {
|
|
58
|
-
// Display project HASHES rather than timestamps: the server's
|
|
59
|
-
// backup `createdAt` is the record creation time (often stale
|
|
60
|
-
// even for a just-pushed backup), so "Server last pushed: <old
|
|
61
|
-
// date>" alongside "server is newer" would look contradictory
|
|
62
|
-
// and undermine the refusal. Hashes are what actually drive the
|
|
63
|
-
// decision and are unambiguous.
|
|
64
225
|
const localShort = (freshness.localLastHash ?? 'unknown').substring(0, 12);
|
|
65
226
|
const serverShort = (freshness.serverProjectHash ?? 'unknown').substring(0, 12);
|
|
66
227
|
console.log();
|
|
@@ -77,120 +238,36 @@ export async function pushBackupCommand(options = {}) {
|
|
|
77
238
|
throw new Error("Push backup aborted: server has a different backup than this machine's last push");
|
|
78
239
|
}
|
|
79
240
|
}
|
|
80
|
-
//
|
|
81
|
-
|
|
241
|
+
// Compile when we don't have output yet AND we're not in fresh mode
|
|
242
|
+
// (fresh mode bypasses the compiler entirely).
|
|
243
|
+
if (!options.fresh && !hasCompilationOutput()) {
|
|
82
244
|
if (options.skipCompile) {
|
|
83
245
|
throw new Error('No compilation output found. Run "lua compile" first or remove --skip-compile flag.');
|
|
84
246
|
}
|
|
85
247
|
writeInfo('No compilation output found. Running compile first...');
|
|
86
248
|
await compileCommand({ sync: false });
|
|
87
249
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
// (The guard's COMPILE_MANAGED_FILES skip still covers the
|
|
98
|
-
// orthogonal case where sync --pull's OWN compile mutates yaml
|
|
99
|
-
// mid-run. That's unavoidable without restructuring compile.)
|
|
100
|
-
reconcileManifestWithDisk();
|
|
101
|
-
// Load manifest
|
|
102
|
-
const manifest = loadManifest();
|
|
103
|
-
if (manifest.projectFiles.length === 0) {
|
|
104
|
-
writeInfo('No project files found in manifest. Nothing to backup.');
|
|
105
|
-
trackEvent('cli_backup_pushed', { skipped: true, skip_reason: 'no_files' });
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
// Calculate project hash
|
|
109
|
-
const projectHash = calculateProjectHash(manifest.projectFiles);
|
|
110
|
-
writeInfo(`Project hash: ${projectHash.substring(0, 12)}...`);
|
|
111
|
-
// Check if backup is already up-to-date locally
|
|
112
|
-
if (!options.force && !isBackupOutOfSync(projectHash, config.backup)) {
|
|
250
|
+
writeProgress('Pushing backup...');
|
|
251
|
+
const result = await runBackupPush({
|
|
252
|
+
apiKey,
|
|
253
|
+
agentId,
|
|
254
|
+
force: options.force ?? false,
|
|
255
|
+
fresh: options.fresh ?? false,
|
|
256
|
+
triggeredBy: 'manual lua push backup',
|
|
257
|
+
});
|
|
258
|
+
if (result.skipped) {
|
|
113
259
|
writeSuccess('Backup is already up-to-date. Use --force to push anyway.');
|
|
114
260
|
trackEvent('cli_backup_pushed', { skipped: true, skip_reason: 'up_to_date' });
|
|
115
261
|
return;
|
|
116
262
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (existsResponse.success && existsResponse.data?.exists && !options.force) {
|
|
122
|
-
// Update local tracking and skip upload
|
|
123
|
-
config.backup = createBackupTracking(projectHash);
|
|
124
|
-
writeYamlConfig(config);
|
|
125
|
-
writeSuccess('Server already has this backup. Local tracking updated.');
|
|
126
|
-
trackEvent('cli_backup_pushed', { skipped: true, skip_reason: 'server_has_backup' });
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
// ===========================================================================
|
|
130
|
-
// PHASE 1: Check which blobs need to be uploaded
|
|
131
|
-
// ===========================================================================
|
|
132
|
-
writeProgress('Checking which files need upload...');
|
|
133
|
-
const allHashes = getAllFileHashes();
|
|
134
|
-
const checkResponse = await backupApi.checkBlobsExist(allHashes);
|
|
135
|
-
if (!checkResponse.success) {
|
|
136
|
-
throw new Error(checkResponse.error?.message || 'Failed to check blob existence');
|
|
137
|
-
}
|
|
138
|
-
const { missing, existing } = checkResponse.data;
|
|
139
|
-
writeInfo(`Files: ${allHashes.length} total, ${existing.length} already stored, ${missing.length} to upload`);
|
|
140
|
-
// ===========================================================================
|
|
141
|
-
// PHASE 2: Upload missing blobs directly to S3
|
|
142
|
-
// ===========================================================================
|
|
143
|
-
if (missing.length > 0) {
|
|
144
|
-
writeProgress(`Uploading ${missing.length} new files to storage...`);
|
|
145
|
-
// Get presigned upload URLs from server
|
|
146
|
-
const urlsResponse = await backupApi.getBlobUploadUrls(missing);
|
|
147
|
-
if (!urlsResponse.success) {
|
|
148
|
-
throw new Error(urlsResponse.error?.message || 'Failed to get upload URLs');
|
|
149
|
-
}
|
|
150
|
-
// Upload directly to S3 (parallel, gzip compressed)
|
|
151
|
-
await uploadBlobsToS3(urlsResponse.data.urls, manifest, process.cwd(), (uploaded, total) => writeProgress(`Uploaded ${uploaded}/${total} files...`));
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
writeInfo('All files already in storage (deduplication working!)');
|
|
155
|
-
}
|
|
156
|
-
// ===========================================================================
|
|
157
|
-
// PHASE 3: Save the manifest
|
|
158
|
-
// ===========================================================================
|
|
159
|
-
writeProgress('Saving backup manifest...');
|
|
160
|
-
const fileRefs = prepareFileRefs();
|
|
161
|
-
const saveResponse = await backupApi.saveManifest({
|
|
162
|
-
projectHash,
|
|
163
|
-
files: fileRefs,
|
|
164
|
-
version: config.skills?.[0]?.version, // Use first skill version if available
|
|
165
|
-
});
|
|
166
|
-
if (!saveResponse.success) {
|
|
167
|
-
throw new Error(saveResponse.error?.message || 'Failed to save manifest');
|
|
168
|
-
}
|
|
169
|
-
// Update local tracking
|
|
170
|
-
config.backup = createBackupTracking(projectHash);
|
|
171
|
-
writeYamlConfig(config);
|
|
172
|
-
// Cache the just-pushed manifest locally so `lua sync --pull` can
|
|
173
|
-
// detect files modified since this push. Best-effort; does not fail
|
|
174
|
-
// the push.
|
|
175
|
-
writeBackupManifestCache({
|
|
176
|
-
lastHash: projectHash,
|
|
177
|
-
lastPushedAt: new Date().toISOString(),
|
|
178
|
-
// External (cross-root) entries are sandboxed during restore and
|
|
179
|
-
// the conflict guard skips them — keeping them out of the cache
|
|
180
|
-
// avoids confusing path collisions and keeps the cache focused on
|
|
181
|
-
// the files we actually compare against disk.
|
|
182
|
-
files: fileRefs.filter((f) => !f.external).map((f) => ({ relativePath: f.relativePath, hash: f.hash })),
|
|
183
|
-
});
|
|
184
|
-
writeSuccess(`Backup pushed successfully!\n` +
|
|
185
|
-
` Files: ${manifest.projectFiles.length}\n` +
|
|
186
|
-
` New uploads: ${missing.length}\n` +
|
|
187
|
-
` Deduplicated: ${existing.length}\n` +
|
|
188
|
-
` Hash: ${projectHash.substring(0, 12)}...`);
|
|
263
|
+
const versionLabel = result.activeVersion > 0 ? ` (v${result.activeVersion})` : '';
|
|
264
|
+
writeSuccess(`Backup pushed successfully${versionLabel}!\n` +
|
|
265
|
+
` New uploads: ${result.filesUploaded}\n` +
|
|
266
|
+
` Hash: ${result.projectHash.substring(0, 12)}...`);
|
|
189
267
|
trackEvent('cli_backup_pushed', {
|
|
190
268
|
force_mode: options.force || false,
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
deduplicated_count: existing.length,
|
|
269
|
+
new_uploads_count: result.filesUploaded,
|
|
270
|
+
active_version: result.activeVersion,
|
|
194
271
|
});
|
|
195
272
|
}, 'push backup');
|
|
196
273
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pushBackup.js","sourceRoot":"","sources":["../../src/commands/pushBackup.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"pushBackup.js","sourceRoot":"","sources":["../../src/commands/pushBackup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Source Commands
|
|
3
|
+
*
|
|
4
|
+
* User-facing commands for inspecting and rolling back to historical
|
|
5
|
+
* source backup versions. Backed by the Phase 1 versioned-manifest
|
|
6
|
+
* endpoints on the server.
|
|
7
|
+
*
|
|
8
|
+
* - `lua source list` — list backup versions for the agent
|
|
9
|
+
* - `lua source rollback --version N` — restore version N's files locally
|
|
10
|
+
* and append them as a new version
|
|
11
|
+
*
|
|
12
|
+
* The rollback command intentionally does NOT mutate the historical
|
|
13
|
+
* record. It downloads v<N>'s files, overwrites the local working copy,
|
|
14
|
+
* and then runs a fresh push that creates v<latest+1>. Append-only
|
|
15
|
+
* history means rolling back is always safe and auditable.
|
|
16
|
+
*/
|
|
17
|
+
import type { BackupManifest } from '../interfaces/backup.js';
|
|
18
|
+
interface ManifestVersionSummary {
|
|
19
|
+
version: number;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
createdBy: string;
|
|
22
|
+
triggeredBy?: string;
|
|
23
|
+
projectHash: string;
|
|
24
|
+
}
|
|
25
|
+
interface ListResponse {
|
|
26
|
+
versions: ManifestVersionSummary[];
|
|
27
|
+
}
|
|
28
|
+
export interface SourceListOptions {
|
|
29
|
+
all: boolean;
|
|
30
|
+
limit: number;
|
|
31
|
+
/** Test seam — bypasses HTTP. */
|
|
32
|
+
fetchVersions?: (apiKey: string, agentId: string, all: boolean) => Promise<ListResponse>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* List historical backup versions for the current agent. Active version
|
|
36
|
+
* is marked with a `*` next to its number. Default cap 50; pass --all
|
|
37
|
+
* to show full history.
|
|
38
|
+
*/
|
|
39
|
+
export declare function sourceListCommand(opts: SourceListOptions): Promise<void>;
|
|
40
|
+
interface RollbackManifestResponse {
|
|
41
|
+
files: BackupManifest['files'];
|
|
42
|
+
activeVersion?: number;
|
|
43
|
+
[key: string]: unknown;
|
|
44
|
+
}
|
|
45
|
+
export interface SourceRollbackOptions {
|
|
46
|
+
version: number;
|
|
47
|
+
force: boolean;
|
|
48
|
+
/** Test seam — bypasses HTTP. */
|
|
49
|
+
fetchManifest?: (apiKey: string, agentId: string, version: number) => Promise<RollbackManifestResponse>;
|
|
50
|
+
/** Test seam — bypasses HTTP. */
|
|
51
|
+
fetchUrls?: (apiKey: string, agentId: string, hashes: string[]) => Promise<{
|
|
52
|
+
urls: Record<string, string>;
|
|
53
|
+
}>;
|
|
54
|
+
/** Test seam — bypasses S3 download. */
|
|
55
|
+
downloadBlobs?: (urls: Record<string, string>) => Promise<Map<string, Buffer>>;
|
|
56
|
+
/** Test seam — bypasses disk writes. */
|
|
57
|
+
restoreFiles?: (manifest: RollbackManifestResponse, blobs: Map<string, Buffer>, targetDir: string) => void;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Roll back to a historical backup version.
|
|
61
|
+
*
|
|
62
|
+
* Append-only history: this does NOT mutate v<N>. It downloads v<N>'s
|
|
63
|
+
* files, overwrites the local working copy, then runs `runBackupPush`
|
|
64
|
+
* with `fresh=true` so the server creates v<latest+1> with v<N>'s
|
|
65
|
+
* contents. Blobs already exist on S3 (content-addressed dedup), so
|
|
66
|
+
* the only new write is the manifest itself.
|
|
67
|
+
*/
|
|
68
|
+
export declare function sourceRollbackCommand(opts: SourceRollbackOptions): Promise<{
|
|
69
|
+
newActiveVersion: number;
|
|
70
|
+
}>;
|
|
71
|
+
export {};
|
|
72
|
+
//# sourceMappingURL=source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/commands/source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAUH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,UAAU,sBAAsB;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,YAAY;IACpB,QAAQ,EAAE,sBAAsB,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;CAC1F;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgC9E;AAcD,UAAU,wBAAwB;IAChC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;IAGvB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,iCAAiC;IACjC,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,wBAAwB,CAAC,CAAC;IACxG,iCAAiC;IACjC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC,CAAC;IAC7G,wCAAwC;IACxC,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/E,wCAAwC;IACxC,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,wBAAwB,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5G;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC;IAAE,gBAAgB,EAAE,MAAM,CAAA;CAAE,CAAC,CA2C9G"}
|