@thinkingai/ae-cli 6.1.18 → 6.1.20
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/README.md +97 -38
- package/README.zh.md +101 -38
- package/dist/{auth-ROB2EDYV.js → auth-FUM37MHF.js} +241 -126
- package/dist/{capability-DKMYUTLC.js → capability-AXFQW5WM.js} +49 -35
- package/dist/{chunk-JHENBQ5B.js → chunk-4P355ZWB.js} +70 -1
- package/dist/chunk-6ZIQV5GW.js +26 -0
- package/dist/{chunk-TUKQZTMI.js → chunk-7K24F7T2.js} +2 -0
- package/dist/{chunk-OO6XR6LK.js → chunk-AMBFK2K3.js} +2 -0
- package/dist/chunk-ATSM5XAW.js +623 -0
- package/dist/{chunk-4XXOWOTA.js → chunk-BBEFP4SB.js} +31 -38
- package/dist/{chunk-BYYS3ANB.js → chunk-CZU3V3DQ.js} +5 -15
- package/dist/{chunk-VTWMIC5L.js → chunk-E2JKXMVO.js} +2 -0
- package/dist/chunk-ECBLHAZO.js +15 -0
- package/dist/{sync-HKIOZXQE.js → chunk-I4WQAEYB.js} +31 -540
- package/dist/{chunk-DWO43OIB.js → chunk-JQ3ENZZH.js} +2 -0
- package/dist/chunk-LNZBEQXW.js +25216 -0
- package/dist/{chunk-ZQ47LWTI.js → chunk-QJQZH5GQ.js} +49 -79
- package/dist/{chunk-QZ3AS4KK.js → chunk-RSG4ONOI.js} +9 -8
- package/dist/{chunk-4NN5IWVN.js → chunk-T6OSFQZD.js} +2 -0
- package/dist/{chunk-Y3LOALAV.js → chunk-TAL6CZH6.js} +8 -7
- package/dist/{chunk-3KWQYGYI.js → chunk-TKHSULJT.js} +2 -0
- package/dist/chunk-VXNMYUXU.js +202 -0
- package/dist/{chunk-3FY3RJ26.js → chunk-WZ6YVQSF.js} +15 -14
- package/dist/{chunk-6EIJSNBD.js → chunk-Y74WTIKJ.js} +2 -0
- package/dist/{community-report-client-C7WDGET3.js → community-report-client-XXWGSBSD.js} +3 -4
- package/dist/{config-BMYZX2UE.js → config-EXUGQN5T.js} +10 -10
- package/dist/{data-integration-QEKDWQDY.js → data-integration-4NZ53OMT.js} +920 -97
- package/dist/index.js +56 -81
- package/dist/{local-data-upload-client-4YYHSYD6.js → local-data-upload-client-KYOKVYZV.js} +3 -4
- package/dist/{memory-I2WXDTV2.js → memory-ATNKZNW5.js} +6 -7
- package/dist/{metadata-I4C2EWUN.js → metadata-VZVC7YMH.js} +10 -11
- package/dist/{model-HLHIEFMU.js → model-E4JMQ4V2.js} +8 -9
- package/dist/{personal-semantic-preference-LIPACBDX.js → personal-semantic-preference-7S2SQ3UV.js} +9 -10
- package/dist/{project-semantic-RT3R2VQD.js → project-semantic-2SZP2OPO.js} +13 -14
- package/dist/sync-YV3E66IF.js +520 -0
- package/dist/{te-agent-BR6VDBNX.js → te-agent-JB5T3PO7.js} +396 -92
- package/dist/{te-analysis-7VUNUYWZ.js → te-analysis-3YJAAT2D.js} +196 -43
- package/dist/{te-community-5DMNKJWY.js → te-community-UDBI672N.js} +12 -34
- package/dist/{te-dataops-6P5IKWNJ.js → te-dataops-ZLYOCXZ4.js} +480 -81
- package/dist/{te-engage-KZPR5R22.js → te-engage-4XG6OJML.js} +88 -16
- package/dist/{te-experiment-6BITX4RD.js → te-experiment-VXUWPINJ.js} +83 -12
- package/dist/te-kb-WYQWHFSC.js +1732 -0
- package/dist/{te-system-FXITO2JG.js → te-system-7G6F2LJA.js} +569 -35
- package/dist/{te-team-ADOC2ROP.js → te-team-E7FBBXMQ.js} +8 -7
- package/dist/{update-YCYCKJOO.js → update-D47BUG25.js} +8 -8
- package/package.json +22 -10
- package/skills/ae-agent/SKILL.md +30 -13
- package/skills/ae-agent/references/agent-distribution.md +94 -0
- package/skills/ae-agent/references/approval-request.md +4 -0
- package/skills/ae-agent/references/command_index.md +9 -2
- package/skills/ae-agent/references/create-automation.md +20 -2
- package/skills/ae-agent/references/get-agent-context.md +70 -0
- package/skills/ae-agent/references/list-automations.md +18 -3
- package/skills/ae-agent/references/list-mcp-credentials.md +1 -1
- package/skills/ae-agent/references/mcp-token.md +3 -3
- package/skills/ae-agent/references/set-mcp-credential.md +0 -1
- package/skills/ae-agent/references/update-automation.md +18 -0
- package/skills/ae-analysis/SKILL.md +11 -2
- package/skills/ae-analysis/references/adhoc_run.md +2 -0
- package/skills/ae-analysis/references/ai_models.md +23 -3
- package/skills/ae-analysis/references/analysis_gateway_assets.md +3 -3
- package/skills/ae-analysis/references/audience_models.md +18 -0
- package/skills/ae-analysis/references/command_index.md +9 -9
- package/skills/ae-analysis/references/cross_source_config.md +84 -0
- package/skills/ae-analysis/references/dashboard_update.md +1 -1
- package/skills/ae-analysis/references/project_timezone_update.md +13 -4
- package/skills/ae-analysis/references/property_create.md +2 -0
- package/skills/ae-analysis/references/super_metadata_batch_create.md +2 -0
- package/skills/ae-analysis/references/user_cluster_models.md +2 -0
- package/skills/ae-analysis/references/user_cluster_update.md +8 -4
- package/skills/ae-analysis/references/user_tag_create.md +30 -2
- package/skills/ae-analysis/references/user_tag_models.md +17 -3
- package/skills/ae-analysis/references/user_tag_update.md +14 -2
- package/skills/ae-data-integration/SKILL.md +3 -1
- package/skills/ae-data-integration/references/dimension-routing.md +36 -0
- package/skills/ae-data-integration/references/error-handling.md +54 -1
- package/skills/ae-data-integration/references/local-analysis.md +2 -0
- package/skills/ae-data-integration/references/source-inspect.md +18 -2
- package/skills/ae-data-integration/references/tracking-plan.md +1 -1
- package/skills/ae-data-integration/references/transform.md +4 -2
- package/skills/ae-data-integration/references/ue-mapping.md +5 -2
- package/skills/ae-data-integration/references/ue-routing.md +40 -1
- package/skills/ae-dataops/SKILL.md +11 -1
- package/skills/ae-dataops/references/dataops-backfill.md +135 -0
- package/skills/ae-engage/SKILL.md +5 -0
- package/skills/ae-engage/references/build-task-save-guide.md +5 -1
- package/skills/ae-engage/references/save-flow.md +37 -1
- package/skills/ae-engage/references/save-task.md +6 -0
- package/skills/ae-experiment/SKILL.md +6 -2
- package/skills/ae-experiment/references/save_metric.md +20 -8
- package/skills/ae-generate-tracking-plan/SKILL.md +25 -13
- package/skills/ae-generate-tracking-plan/references/business-dimension-mapping.md +1 -1
- package/skills/ae-kb/SKILL.md +218 -36
- package/skills/ae-kb/references/query-workflow.md +59 -29
- package/skills/ae-kb/references/versions.md +46 -0
- package/skills/ae-system/SKILL.md +29 -31
- package/skills/ae-system/references/channel-management.md +303 -0
- package/skills/ae-use-agent/SKILL.md +42 -0
- package/skills/ae-use-agent/references/local-agent.md +114 -0
- package/dist/auth-GBMV6TEJ.js +0 -14
- package/dist/capability-HYVVPG25.js +0 -352
- package/dist/chunk-EFH4XWYC.js +0 -556
- package/dist/chunk-J2DEBMRF.js +0 -313
- package/dist/chunk-JRJY5DMJ.js +0 -71
- package/dist/chunk-OMPRXM3V.js +0 -349
- package/dist/chunk-QNOLN2LJ.js +0 -509
- package/dist/chunk-RJDU7NYP.js +0 -1198
- package/dist/chunk-RNAALWJK.js +0 -98
- package/dist/chunk-SERWF6G5.js +0 -13
- package/dist/chunk-UW5UN47B.js +0 -70
- package/dist/chunk-ZQKDZXDO.js +0 -317
- package/dist/client-L2YDMHQ6.js +0 -203
- package/dist/memory-3ORCR7JH.js +0 -893
- package/dist/metadata-VUOQJE26.js +0 -339
- package/dist/model-UGRDX4MW.js +0 -139
- package/dist/personal-semantic-preference-OEISBRHM.js +0 -239
- package/dist/project-semantic-FFPWFPIW.js +0 -1114
- package/dist/sync-TFHU2UTG.js +0 -10261
- package/dist/te-agent-VLYOV7S4.js +0 -3894
- package/dist/te-analysis-4YGQL5RC.js +0 -9357
- package/dist/te-community-ISDQWJU7.js +0 -1859
- package/dist/te-dataops-CVULXNVB.js +0 -2209
- package/dist/te-engage-N5WI32H6.js +0 -4898
- package/dist/te-experiment-UVR4HLND.js +0 -988
- package/dist/te-kb-RCLSSH2Q.js +0 -935
- package/dist/te-system-K2GYMCTB.js +0 -2213
- package/skills/ae-agent/references/auto-provision-mcp-credentials.md +0 -57
package/dist/chunk-RNAALWJK.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getCliToken
|
|
3
|
-
} from "./chunk-J2DEBMRF.js";
|
|
4
|
-
import {
|
|
5
|
-
safeJsonParse
|
|
6
|
-
} from "./chunk-3FY3RJ26.js";
|
|
7
|
-
import {
|
|
8
|
-
PermissionError
|
|
9
|
-
} from "./chunk-UW5UN47B.js";
|
|
10
|
-
|
|
11
|
-
// src/core/mcp-access.ts
|
|
12
|
-
function buildUrl(host, path, params = {}) {
|
|
13
|
-
const base = host.replace(/\/$/, "");
|
|
14
|
-
const p = path.startsWith("/") ? path : `/${path}`;
|
|
15
|
-
const url = new URL(`${base}${p}`);
|
|
16
|
-
for (const [key, value] of Object.entries(params)) {
|
|
17
|
-
if (value !== void 0 && value !== null) {
|
|
18
|
-
url.searchParams.set(key, String(value));
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return url.toString();
|
|
22
|
-
}
|
|
23
|
-
function parseKbResponse(resp, text) {
|
|
24
|
-
if (resp.status === 403) {
|
|
25
|
-
let msg = "Permission denied for this resource (HTTP 403)";
|
|
26
|
-
try {
|
|
27
|
-
const d = safeJsonParse(text);
|
|
28
|
-
const m = d && (typeof d.error === "string" ? d.error : d.error?.message || d.message);
|
|
29
|
-
if (m && typeof m === "string") msg = m;
|
|
30
|
-
} catch {
|
|
31
|
-
}
|
|
32
|
-
throw new PermissionError(msg);
|
|
33
|
-
}
|
|
34
|
-
if (resp.status === 401) {
|
|
35
|
-
throw new Error(
|
|
36
|
-
`KB API token auth failed: HTTP ${resp.status} ${resp.statusText}`
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
const data = safeJsonParse(text);
|
|
40
|
-
if (data.return_code === -1001) {
|
|
41
|
-
throw new Error(
|
|
42
|
-
`KB API token auth failed: ${data.return_message || "unauthorized"} (code: ${data.return_code})`
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
if (data.return_code !== 0 && data.return_code !== void 0) {
|
|
46
|
-
throw new Error(
|
|
47
|
-
`AE API error: ${data.return_message || "unknown"} (code: ${data.return_code})`
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
if (data && typeof data === "object" && typeof data.error === "string" && data.error.trim() && data.return_code === void 0 && data.success === void 0) {
|
|
51
|
-
throw new Error(data.error);
|
|
52
|
-
}
|
|
53
|
-
return data.data !== void 0 ? data.data : data;
|
|
54
|
-
}
|
|
55
|
-
async function fetchWithCliToken(input, init, cliToken) {
|
|
56
|
-
const headers = new Headers(init.headers);
|
|
57
|
-
headers.set("cli-token", cliToken);
|
|
58
|
-
const resp = await fetch(input, { ...init, headers });
|
|
59
|
-
return parseKbResponse(resp, await resp.text());
|
|
60
|
-
}
|
|
61
|
-
async function getAuthHeaders(ctx) {
|
|
62
|
-
const cliToken = await getCliToken(ctx.host());
|
|
63
|
-
return { "cli-token": cliToken };
|
|
64
|
-
}
|
|
65
|
-
async function kbApi(ctx, method, path, params = {}, body) {
|
|
66
|
-
const host = ctx.host();
|
|
67
|
-
const cliToken = await getCliToken(host);
|
|
68
|
-
const upperMethod = method.toUpperCase();
|
|
69
|
-
return fetchWithCliToken(
|
|
70
|
-
buildUrl(host, path, params),
|
|
71
|
-
{
|
|
72
|
-
method: upperMethod,
|
|
73
|
-
headers: {
|
|
74
|
-
"Content-Type": "application/json"
|
|
75
|
-
},
|
|
76
|
-
body: upperMethod === "GET" ? void 0 : JSON.stringify(body ?? {})
|
|
77
|
-
},
|
|
78
|
-
cliToken
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
async function kbUpload(ctx, path, form, params = {}) {
|
|
82
|
-
const host = ctx.host();
|
|
83
|
-
const cliToken = await getCliToken(host);
|
|
84
|
-
return fetchWithCliToken(
|
|
85
|
-
buildUrl(host, path, params),
|
|
86
|
-
{
|
|
87
|
-
method: "POST",
|
|
88
|
-
body: form
|
|
89
|
-
},
|
|
90
|
-
cliToken
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export {
|
|
95
|
-
getAuthHeaders,
|
|
96
|
-
kbApi,
|
|
97
|
-
kbUpload
|
|
98
|
-
};
|
package/dist/chunk-SERWF6G5.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isAeSandboxRuntime
|
|
3
|
-
} from "./chunk-3FY3RJ26.js";
|
|
4
|
-
|
|
5
|
-
// src/core/internal-call-source.ts
|
|
6
|
-
function internalCallSourceHeaders() {
|
|
7
|
-
const source = isAeSandboxRuntime() ? "te-agent" : "ae-cli";
|
|
8
|
-
return { "X-Source": source };
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export {
|
|
12
|
-
internalCallSourceHeaders
|
|
13
|
-
};
|
package/dist/chunk-UW5UN47B.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
// src/core/errors.ts
|
|
2
|
-
var PermissionError = class extends Error {
|
|
3
|
-
constructor(message, code, hint) {
|
|
4
|
-
super(message);
|
|
5
|
-
this.code = code;
|
|
6
|
-
this.hint = hint;
|
|
7
|
-
this.name = "PermissionError";
|
|
8
|
-
}
|
|
9
|
-
code;
|
|
10
|
-
hint;
|
|
11
|
-
};
|
|
12
|
-
var CliValidationError = class extends Error {
|
|
13
|
-
code;
|
|
14
|
-
hint;
|
|
15
|
-
location;
|
|
16
|
-
constructor(message, options = {}) {
|
|
17
|
-
super(message);
|
|
18
|
-
this.name = "CliValidationError";
|
|
19
|
-
this.code = options.code;
|
|
20
|
-
this.hint = options.hint;
|
|
21
|
-
if (options.location) {
|
|
22
|
-
const location = {};
|
|
23
|
-
if (typeof options.location.segment === "number" && Number.isFinite(options.location.segment)) {
|
|
24
|
-
location.segment = options.location.segment;
|
|
25
|
-
}
|
|
26
|
-
if (typeof options.location.record === "number" && Number.isFinite(options.location.record)) {
|
|
27
|
-
location.record = options.location.record;
|
|
28
|
-
}
|
|
29
|
-
if (typeof options.location.field === "string" && options.location.field.length > 0) {
|
|
30
|
-
location.field = options.location.field;
|
|
31
|
-
}
|
|
32
|
-
if (Object.keys(location).length > 0) this.location = location;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
var CommunityReportError = class extends Error {
|
|
37
|
-
code;
|
|
38
|
-
httpStatus;
|
|
39
|
-
hint;
|
|
40
|
-
meta;
|
|
41
|
-
constructor(message, options = {}) {
|
|
42
|
-
super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
|
|
43
|
-
this.name = "CommunityReportError";
|
|
44
|
-
this.code = options.code;
|
|
45
|
-
this.httpStatus = options.httpStatus;
|
|
46
|
-
this.hint = options.hint;
|
|
47
|
-
this.meta = options.meta;
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
var LocalDataUploadError = class extends Error {
|
|
51
|
-
code;
|
|
52
|
-
httpStatus;
|
|
53
|
-
hint;
|
|
54
|
-
meta;
|
|
55
|
-
constructor(message, options = {}) {
|
|
56
|
-
super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
|
|
57
|
-
this.name = "LocalDataUploadError";
|
|
58
|
-
this.code = options.code;
|
|
59
|
-
this.httpStatus = options.httpStatus;
|
|
60
|
-
this.hint = options.hint;
|
|
61
|
-
this.meta = options.meta;
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export {
|
|
66
|
-
PermissionError,
|
|
67
|
-
CliValidationError,
|
|
68
|
-
CommunityReportError,
|
|
69
|
-
LocalDataUploadError
|
|
70
|
-
};
|
package/dist/chunk-ZQKDZXDO.js
DELETED
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getActiveHost,
|
|
3
|
-
getConfigDir,
|
|
4
|
-
normalizeUrl,
|
|
5
|
-
safeJsonParse
|
|
6
|
-
} from "./chunk-3FY3RJ26.js";
|
|
7
|
-
import {
|
|
8
|
-
logger
|
|
9
|
-
} from "./chunk-JHENBQ5B.js";
|
|
10
|
-
|
|
11
|
-
// src/core/secure-store.ts
|
|
12
|
-
import crypto from "crypto";
|
|
13
|
-
import fs from "fs";
|
|
14
|
-
import os from "os";
|
|
15
|
-
import path from "path";
|
|
16
|
-
import { execFileSync } from "child_process";
|
|
17
|
-
var STORE_DIR_SUFFIX = "secure-tokens";
|
|
18
|
-
var SCRYPT_SALT = Buffer.from("ae-cli-secure-store-v1");
|
|
19
|
-
var SCRYPT_N = 16384;
|
|
20
|
-
var SCRYPT_R = 8;
|
|
21
|
-
var SCRYPT_P = 1;
|
|
22
|
-
var KEY_LEN = 32;
|
|
23
|
-
var NONCE_LEN = 12;
|
|
24
|
-
var REFRESH_TOKEN_PATH = "/v1/oauth/refreshForToken";
|
|
25
|
-
function getMachineId() {
|
|
26
|
-
if (process.platform === "darwin") {
|
|
27
|
-
try {
|
|
28
|
-
const out = execFileSync(
|
|
29
|
-
"ioreg",
|
|
30
|
-
["-rd1", "-c", "IOPlatformExpertDevice"],
|
|
31
|
-
{ encoding: "utf8", timeout: 3e3 }
|
|
32
|
-
);
|
|
33
|
-
const m = out.match(/IOPlatformUUID.*?=.*?"([0-9A-F-]{36})"/i);
|
|
34
|
-
if (m?.[1]) return `darwin:${m[1]}`;
|
|
35
|
-
} catch {
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
if (process.platform === "linux") {
|
|
39
|
-
for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
|
|
40
|
-
try {
|
|
41
|
-
const id = fs.readFileSync(p, "utf8").trim();
|
|
42
|
-
if (id) return `linux:${id}`;
|
|
43
|
-
} catch {
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
if (process.platform === "win32") {
|
|
48
|
-
try {
|
|
49
|
-
const out = execFileSync(
|
|
50
|
-
"reg",
|
|
51
|
-
["query", "HKLM\\SOFTWARE\\Microsoft\\Cryptography", "/v", "MachineGuid"],
|
|
52
|
-
{ encoding: "utf8", timeout: 3e3 }
|
|
53
|
-
);
|
|
54
|
-
const m = out.match(/MachineGuid\s+REG_SZ\s+([0-9a-f-]+)/i);
|
|
55
|
-
if (m?.[1]) return `win32:${m[1]}`;
|
|
56
|
-
} catch {
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
const fallback = `${os.homedir()}|${os.hostname()}|${os.platform()}`;
|
|
60
|
-
logger.warn("secure-store: using fallback machine-id (HOME+hostname+platform)");
|
|
61
|
-
return `fallback:${fallback}`;
|
|
62
|
-
}
|
|
63
|
-
var _cachedMachineId;
|
|
64
|
-
function getCachedMachineId() {
|
|
65
|
-
if (!_cachedMachineId) _cachedMachineId = getMachineId();
|
|
66
|
-
return _cachedMachineId;
|
|
67
|
-
}
|
|
68
|
-
function deriveKey(machineId) {
|
|
69
|
-
return crypto.scryptSync(machineId, SCRYPT_SALT, KEY_LEN, {
|
|
70
|
-
N: SCRYPT_N,
|
|
71
|
-
r: SCRYPT_R,
|
|
72
|
-
p: SCRYPT_P
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
var _cachedKey;
|
|
76
|
-
function getKey() {
|
|
77
|
-
if (!_cachedKey) _cachedKey = deriveKey(getCachedMachineId());
|
|
78
|
-
return _cachedKey;
|
|
79
|
-
}
|
|
80
|
-
function encrypt(plaintext) {
|
|
81
|
-
const key = getKey();
|
|
82
|
-
const nonce = crypto.randomBytes(NONCE_LEN);
|
|
83
|
-
const cipher = crypto.createCipheriv("aes-256-gcm", key, nonce);
|
|
84
|
-
const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
85
|
-
const tag = cipher.getAuthTag();
|
|
86
|
-
return {
|
|
87
|
-
nonce: nonce.toString("hex"),
|
|
88
|
-
tag: tag.toString("hex"),
|
|
89
|
-
data: encrypted.toString("hex")
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
function decrypt(blob) {
|
|
93
|
-
const key = getKey();
|
|
94
|
-
const nonce = Buffer.from(blob.nonce, "hex");
|
|
95
|
-
const tag = Buffer.from(blob.tag, "hex");
|
|
96
|
-
const data = Buffer.from(blob.data, "hex");
|
|
97
|
-
const decipher = crypto.createDecipheriv("aes-256-gcm", key, nonce);
|
|
98
|
-
decipher.setAuthTag(tag);
|
|
99
|
-
const decrypted = Buffer.concat([decipher.update(data), decipher.final()]);
|
|
100
|
-
return decrypted.toString("utf8");
|
|
101
|
-
}
|
|
102
|
-
function storeDir() {
|
|
103
|
-
return path.join(getConfigDir(), STORE_DIR_SUFFIX);
|
|
104
|
-
}
|
|
105
|
-
function tokenFilePath(host) {
|
|
106
|
-
const safe = host.replace(/^https?:\/\//, "").replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
107
|
-
return path.join(storeDir(), `${safe}.enc.json`);
|
|
108
|
-
}
|
|
109
|
-
function ensureStoreDir() {
|
|
110
|
-
const dir = storeDir();
|
|
111
|
-
if (!fs.existsSync(dir)) {
|
|
112
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
113
|
-
try {
|
|
114
|
-
fs.chmodSync(dir, 448);
|
|
115
|
-
} catch {
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
function save(host, payload) {
|
|
120
|
-
ensureStoreDir();
|
|
121
|
-
const plaintext = JSON.stringify(payload);
|
|
122
|
-
const blob = encrypt(plaintext);
|
|
123
|
-
const filePath = tokenFilePath(host);
|
|
124
|
-
fs.writeFileSync(filePath, JSON.stringify(blob, null, 2), { encoding: "utf8" });
|
|
125
|
-
try {
|
|
126
|
-
fs.chmodSync(filePath, 384);
|
|
127
|
-
} catch {
|
|
128
|
-
}
|
|
129
|
-
logger.info(`secure-store: token saved for ${host}`);
|
|
130
|
-
}
|
|
131
|
-
function load(host) {
|
|
132
|
-
const filePath = tokenFilePath(host);
|
|
133
|
-
if (!fs.existsSync(filePath)) return null;
|
|
134
|
-
try {
|
|
135
|
-
const raw = fs.readFileSync(filePath, "utf8");
|
|
136
|
-
const blob = safeJsonParse(raw);
|
|
137
|
-
if (!blob?.nonce || !blob?.tag || !blob?.data) {
|
|
138
|
-
logger.warn(`secure-store: malformed blob for ${host}`);
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
const plaintext = decrypt(blob);
|
|
142
|
-
return JSON.parse(plaintext);
|
|
143
|
-
} catch (e) {
|
|
144
|
-
logger.error(`secure-store: decrypt failed for ${host}: ${e.message}`);
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
function clear(host) {
|
|
149
|
-
const filePath = tokenFilePath(host);
|
|
150
|
-
if (fs.existsSync(filePath)) {
|
|
151
|
-
fs.rmSync(filePath);
|
|
152
|
-
logger.info(`secure-store: token cleared for ${host}`);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
async function getValidAccessToken(host) {
|
|
156
|
-
const payload = load(host);
|
|
157
|
-
if (!payload) {
|
|
158
|
-
throw new SecureStoreAuthError(
|
|
159
|
-
`No stored token for ${host}. Please run: ae-cli auth login`
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
const expiresAt = new Date(payload.accessExpiresAt).getTime();
|
|
163
|
-
const now = Date.now();
|
|
164
|
-
const needsRefresh = expiresAt - now < 6e4;
|
|
165
|
-
if (!needsRefresh) {
|
|
166
|
-
return payload.accessToken;
|
|
167
|
-
}
|
|
168
|
-
if (payload.refreshToken) {
|
|
169
|
-
logger.info(`secure-store: access token past local expiry for ${host}, refreshing\u2026`);
|
|
170
|
-
const refreshed = await doRefresh(host, payload.refreshToken);
|
|
171
|
-
save(host, { ...refreshed, cliToken: payload.cliToken });
|
|
172
|
-
logger.info(`secure-store: token refreshed for ${host}`);
|
|
173
|
-
return refreshed.accessToken;
|
|
174
|
-
}
|
|
175
|
-
logger.info(`secure-store: ${host} past static local expiry, no refresh token \u2014 returning stored token, server validates (lazy)`);
|
|
176
|
-
return payload.accessToken;
|
|
177
|
-
}
|
|
178
|
-
function loadCliToken(host) {
|
|
179
|
-
return load(host)?.cliToken ?? null;
|
|
180
|
-
}
|
|
181
|
-
async function doRefresh(host, refreshToken) {
|
|
182
|
-
const base = host.replace(/\/+$/, "");
|
|
183
|
-
const url = `${base}${REFRESH_TOKEN_PATH}`;
|
|
184
|
-
let resp;
|
|
185
|
-
try {
|
|
186
|
-
resp = await fetch(url, {
|
|
187
|
-
method: "POST",
|
|
188
|
-
headers: { "Content-Type": "application/json" },
|
|
189
|
-
body: JSON.stringify({ refreshToken })
|
|
190
|
-
});
|
|
191
|
-
} catch (e) {
|
|
192
|
-
throw new SecureStoreAuthError(
|
|
193
|
-
`Token refresh network error for ${host}: ${e.message}. Please run: ae-cli auth login`
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
if (!resp.ok) {
|
|
197
|
-
throw new SecureStoreAuthError(
|
|
198
|
-
`Token refresh failed (HTTP ${resp.status}) for ${host}. Please run: ae-cli auth login`
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
let body;
|
|
202
|
-
try {
|
|
203
|
-
body = safeJsonParse(await resp.text());
|
|
204
|
-
} catch {
|
|
205
|
-
throw new SecureStoreAuthError(
|
|
206
|
-
`Token refresh returned invalid JSON for ${host}. Please run: ae-cli auth login`
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
if (body.return_code !== 0) {
|
|
210
|
-
throw new SecureStoreAuthError(
|
|
211
|
-
`Token refresh error (${body.return_code}: ${body.return_message ?? "unknown"}) for ${host}. Please run: ae-cli auth login`
|
|
212
|
-
);
|
|
213
|
-
}
|
|
214
|
-
const d = body.data;
|
|
215
|
-
const newAccess = d?.accessToken ?? d?.access_token ?? "";
|
|
216
|
-
const newRefresh = d?.refreshToken ?? d?.refresh_token ?? refreshToken;
|
|
217
|
-
const expiresIn = d?.expiresIn ?? d?.expires_in ?? 72e3;
|
|
218
|
-
if (!newAccess) {
|
|
219
|
-
throw new SecureStoreAuthError(
|
|
220
|
-
`Token refresh returned empty accessToken for ${host}. Please run: ae-cli auth login`
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
return {
|
|
224
|
-
accessToken: newAccess,
|
|
225
|
-
refreshToken: newRefresh,
|
|
226
|
-
accessExpiresAt: new Date(Date.now() + expiresIn * 1e3).toISOString()
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
var SecureStoreAuthError = class extends Error {
|
|
230
|
-
constructor(message) {
|
|
231
|
-
super(message);
|
|
232
|
-
this.name = "SecureStoreAuthError";
|
|
233
|
-
}
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
// src/core/host-guidance.ts
|
|
237
|
-
var AE_TRIAL_URL = "https://thinkingai.cn/request-demo";
|
|
238
|
-
var AE_HOST_SETUP_COMMAND = "ae-cli config set-host <url>";
|
|
239
|
-
function missingAeHostHint(additionalAction) {
|
|
240
|
-
const actions = [
|
|
241
|
-
`If your organization already uses AgenticEngine, ask your administrator for its AE URL, then run: ${AE_HOST_SETUP_COMMAND}.`,
|
|
242
|
-
`If you do not have an AgenticEngine environment, request a trial: ${AE_TRIAL_URL}`
|
|
243
|
-
];
|
|
244
|
-
if (additionalAction) actions.push(additionalAction);
|
|
245
|
-
return actions.join(" ");
|
|
246
|
-
}
|
|
247
|
-
function missingAeHostGuidance() {
|
|
248
|
-
return {
|
|
249
|
-
configure_host: AE_HOST_SETUP_COMMAND,
|
|
250
|
-
existing_customer: "Ask your AgenticEngine administrator for the AE environment URL.",
|
|
251
|
-
request_trial_url: AE_TRIAL_URL
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// src/core/auth.ts
|
|
256
|
-
function resolveHost(hostOverride) {
|
|
257
|
-
if (hostOverride) return normalizeUrl(hostOverride);
|
|
258
|
-
return getActiveHost();
|
|
259
|
-
}
|
|
260
|
-
async function validateToken(token, hostUrl) {
|
|
261
|
-
const url = `${hostUrl}/v1/oauth/checkToken`;
|
|
262
|
-
try {
|
|
263
|
-
const resp = await fetch(url, {
|
|
264
|
-
method: "POST",
|
|
265
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
266
|
-
body: new URLSearchParams({ accessToken: token }).toString()
|
|
267
|
-
});
|
|
268
|
-
const respJson = safeJsonParse(await resp.text());
|
|
269
|
-
return respJson?.return_code === 0;
|
|
270
|
-
} catch {
|
|
271
|
-
return false;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
function invalidateAccessTokenForRetry(hostUrl) {
|
|
275
|
-
const payload = load(hostUrl);
|
|
276
|
-
if (!payload) return;
|
|
277
|
-
save(hostUrl, { ...payload, accessExpiresAt: (/* @__PURE__ */ new Date(0)).toISOString() });
|
|
278
|
-
logger.info(`auth: marked access token stale for retry on ${hostUrl}`);
|
|
279
|
-
}
|
|
280
|
-
async function getToken(hostUrl) {
|
|
281
|
-
if (!hostUrl) {
|
|
282
|
-
throw new Error(
|
|
283
|
-
`No AE host configured.
|
|
284
|
-
` + missingAeHostHint()
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
try {
|
|
288
|
-
const secureToken = await getValidAccessToken(hostUrl);
|
|
289
|
-
logger.info(`Using secure-store token for ${hostUrl}`);
|
|
290
|
-
return secureToken;
|
|
291
|
-
} catch (e) {
|
|
292
|
-
if (e instanceof SecureStoreAuthError) {
|
|
293
|
-
logger.info(`secure-store: ${e.message}`);
|
|
294
|
-
} else {
|
|
295
|
-
logger.warn(`secure-store unexpected error: ${e.message}`);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
throw new Error(
|
|
299
|
-
`Cannot obtain token for ${hostUrl}.
|
|
300
|
-
Run: ae-cli auth login (device code flow, cross-platform)`
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
export {
|
|
305
|
-
save,
|
|
306
|
-
load,
|
|
307
|
-
clear,
|
|
308
|
-
loadCliToken,
|
|
309
|
-
SecureStoreAuthError,
|
|
310
|
-
AE_TRIAL_URL,
|
|
311
|
-
missingAeHostHint,
|
|
312
|
-
missingAeHostGuidance,
|
|
313
|
-
resolveHost,
|
|
314
|
-
validateToken,
|
|
315
|
-
invalidateAccessTokenForRetry,
|
|
316
|
-
getToken
|
|
317
|
-
};
|
package/dist/client-L2YDMHQ6.js
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SecureStoreAuthError,
|
|
3
|
-
getToken,
|
|
4
|
-
invalidateAccessTokenForRetry,
|
|
5
|
-
resolveHost
|
|
6
|
-
} from "./chunk-ZQKDZXDO.js";
|
|
7
|
-
import {
|
|
8
|
-
safeJsonParse
|
|
9
|
-
} from "./chunk-3FY3RJ26.js";
|
|
10
|
-
import {
|
|
11
|
-
logger
|
|
12
|
-
} from "./chunk-JHENBQ5B.js";
|
|
13
|
-
|
|
14
|
-
// src/core/client.ts
|
|
15
|
-
import WebSocket from "ws";
|
|
16
|
-
import { randomBytes } from "crypto";
|
|
17
|
-
function genRequestId(prefix) {
|
|
18
|
-
const rand = randomBytes(4).toString("base64url").slice(0, 8);
|
|
19
|
-
return `${prefix}@@${rand}`;
|
|
20
|
-
}
|
|
21
|
-
function buildUrl(baseUrl, modulePath, params = {}) {
|
|
22
|
-
const base = baseUrl.replace(/\/$/, "");
|
|
23
|
-
const p = modulePath.startsWith("/") ? modulePath : `/${modulePath}`;
|
|
24
|
-
const url = new URL(`${base}${p}`);
|
|
25
|
-
for (const [k, v] of Object.entries(params)) {
|
|
26
|
-
if (v !== void 0 && v !== null) url.searchParams.set(k, String(v));
|
|
27
|
-
}
|
|
28
|
-
return url.toString();
|
|
29
|
-
}
|
|
30
|
-
function buildWsUrl(baseUrl, token) {
|
|
31
|
-
const base = baseUrl.replace(/\/$/, "");
|
|
32
|
-
const wsBase = base.replace(/^https:\/\//, "wss://").replace(/^http:\/\//, "ws://");
|
|
33
|
-
return `${wsBase}/v1/ta-websocket/query/${token}`;
|
|
34
|
-
}
|
|
35
|
-
async function request(method, modulePath, params = {}, body = null, retry = true, hostUrl) {
|
|
36
|
-
const resolvedHost = resolveHost(hostUrl);
|
|
37
|
-
const token = await getToken(resolvedHost);
|
|
38
|
-
const url = buildUrl(resolvedHost, modulePath, params);
|
|
39
|
-
const headers = {
|
|
40
|
-
"Authorization": `bearer ${token}`,
|
|
41
|
-
"Content-Type": "application/json"
|
|
42
|
-
};
|
|
43
|
-
const options = { method, headers };
|
|
44
|
-
if (body && method !== "GET") options.body = JSON.stringify(body);
|
|
45
|
-
const resp = await fetch(url, options);
|
|
46
|
-
const text = await resp.text();
|
|
47
|
-
const data = safeJsonParse(text);
|
|
48
|
-
logger.api(method, url, resp.status, body, data);
|
|
49
|
-
if (resp.status === 401) {
|
|
50
|
-
if (retry) {
|
|
51
|
-
invalidateAccessTokenForRetry(resolvedHost);
|
|
52
|
-
return request(method, modulePath, params, body, false, resolvedHost);
|
|
53
|
-
}
|
|
54
|
-
throw new SecureStoreAuthError(`Session expired or unauthorized for ${resolvedHost}. Please run: ae-cli auth login`);
|
|
55
|
-
}
|
|
56
|
-
if (resp.status === 403) {
|
|
57
|
-
const m = data && typeof data === "object" && typeof data.error === "string" ? data.error : "Permission denied";
|
|
58
|
-
throw new Error(m);
|
|
59
|
-
}
|
|
60
|
-
if (data.return_code === -1001 && retry) {
|
|
61
|
-
invalidateAccessTokenForRetry(resolvedHost);
|
|
62
|
-
return request(method, modulePath, params, body, false, resolvedHost);
|
|
63
|
-
}
|
|
64
|
-
if (data.return_code !== 0 && data.return_code !== void 0) {
|
|
65
|
-
throw new Error(`AE API error: ${data.return_message || "unknown"} (code: ${data.return_code})`);
|
|
66
|
-
}
|
|
67
|
-
return data.data !== void 0 ? data.data : data;
|
|
68
|
-
}
|
|
69
|
-
async function httpGet(modulePath, params = {}, hostUrl) {
|
|
70
|
-
return request("GET", modulePath, params, null, true, hostUrl);
|
|
71
|
-
}
|
|
72
|
-
async function httpPost(modulePath, params = {}, body, hostUrl) {
|
|
73
|
-
return request("POST", modulePath, params, body ?? {}, true, hostUrl);
|
|
74
|
-
}
|
|
75
|
-
async function httpRequest(method, modulePath, params = {}, body, hostUrl) {
|
|
76
|
-
return request(method.toUpperCase(), modulePath, params, body ?? {}, true, hostUrl);
|
|
77
|
-
}
|
|
78
|
-
async function httpDelete(modulePath, params = {}, body, hostUrl) {
|
|
79
|
-
return request("DELETE", modulePath, params, body ?? null, true, hostUrl);
|
|
80
|
-
}
|
|
81
|
-
async function uploadRequest(modulePath, form, params = {}, retry = true, hostUrl) {
|
|
82
|
-
const resolvedHost = resolveHost(hostUrl);
|
|
83
|
-
const token = await getToken(resolvedHost);
|
|
84
|
-
const url = buildUrl(resolvedHost, modulePath, params);
|
|
85
|
-
const headers = {
|
|
86
|
-
"Authorization": `bearer ${token}`
|
|
87
|
-
};
|
|
88
|
-
const resp = await fetch(url, { method: "POST", headers, body: form });
|
|
89
|
-
const text = await resp.text();
|
|
90
|
-
const data = safeJsonParse(text);
|
|
91
|
-
logger.api("POST", url, resp.status, { upload: true }, data);
|
|
92
|
-
if (resp.status === 401) {
|
|
93
|
-
if (retry) {
|
|
94
|
-
invalidateAccessTokenForRetry(resolvedHost);
|
|
95
|
-
return uploadRequest(modulePath, form, params, false, resolvedHost);
|
|
96
|
-
}
|
|
97
|
-
throw new SecureStoreAuthError(`Session expired or unauthorized for ${resolvedHost}. Please run: ae-cli auth login`);
|
|
98
|
-
}
|
|
99
|
-
if (resp.status === 403) {
|
|
100
|
-
const m = data && typeof data === "object" && typeof data.error === "string" ? data.error : "Permission denied";
|
|
101
|
-
throw new Error(m);
|
|
102
|
-
}
|
|
103
|
-
if (data.return_code === -1001 && retry) {
|
|
104
|
-
invalidateAccessTokenForRetry(resolvedHost);
|
|
105
|
-
return uploadRequest(modulePath, form, params, false, resolvedHost);
|
|
106
|
-
}
|
|
107
|
-
if (data.return_code !== 0 && data.return_code !== void 0) {
|
|
108
|
-
throw new Error(`AE API error: ${data.return_message || "unknown"} (code: ${data.return_code})`);
|
|
109
|
-
}
|
|
110
|
-
return data.data !== void 0 ? data.data : data;
|
|
111
|
-
}
|
|
112
|
-
async function httpUpload(modulePath, form, params = {}, hostUrl) {
|
|
113
|
-
return uploadRequest(modulePath, form, params, true, hostUrl);
|
|
114
|
-
}
|
|
115
|
-
async function wsQueryOnce(projectId, requestId, qp, eventModel, options = {}, token, hostUrl, timeoutMs = 3e4) {
|
|
116
|
-
const wsUrl = buildWsUrl(hostUrl, token);
|
|
117
|
-
return new Promise((resolve, reject) => {
|
|
118
|
-
const ws = new WebSocket(wsUrl);
|
|
119
|
-
const timer = setTimeout(() => {
|
|
120
|
-
ws.close();
|
|
121
|
-
reject(new Error(`WebSocket query timed out after ${timeoutMs}ms`));
|
|
122
|
-
}, timeoutMs);
|
|
123
|
-
ws.on("open", () => {
|
|
124
|
-
const payload = {
|
|
125
|
-
requestId,
|
|
126
|
-
projectId,
|
|
127
|
-
eventModel,
|
|
128
|
-
qp: typeof qp === "string" ? qp : JSON.stringify(qp),
|
|
129
|
-
searchSource: options.searchSource || "reportQuery",
|
|
130
|
-
querySource: options.querySource || "single_report",
|
|
131
|
-
contentTranslate: options.contentTranslate !== false,
|
|
132
|
-
...options.extra || {}
|
|
133
|
-
};
|
|
134
|
-
ws.send(JSON.stringify(["data", payload, { channel: "ta" }]));
|
|
135
|
-
});
|
|
136
|
-
ws.on("message", (raw) => {
|
|
137
|
-
try {
|
|
138
|
-
const msg = safeJsonParse(raw.toString());
|
|
139
|
-
if (Array.isArray(msg) && msg.length >= 2) {
|
|
140
|
-
const data = msg[1];
|
|
141
|
-
if (data.requestId === requestId && data.progress === 100) {
|
|
142
|
-
clearTimeout(timer);
|
|
143
|
-
ws.close();
|
|
144
|
-
resolve(data);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
} catch {
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
ws.on("error", (err) => {
|
|
151
|
-
clearTimeout(timer);
|
|
152
|
-
reject(err);
|
|
153
|
-
});
|
|
154
|
-
ws.on("close", () => {
|
|
155
|
-
clearTimeout(timer);
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
async function wsQuery(projectId, requestId, qp, eventModel, options = {}, hostUrl) {
|
|
160
|
-
const resolvedHost = resolveHost(hostUrl);
|
|
161
|
-
const token = await getToken(resolvedHost);
|
|
162
|
-
try {
|
|
163
|
-
return await wsQueryOnce(projectId, requestId, qp, eventModel, options, token, resolvedHost);
|
|
164
|
-
} catch (err) {
|
|
165
|
-
const msg = err.message || "";
|
|
166
|
-
if (msg.includes("401") || msg.includes("403") || msg.includes("auth")) {
|
|
167
|
-
invalidateAccessTokenForRetry(resolvedHost);
|
|
168
|
-
const newToken = await getToken(resolvedHost);
|
|
169
|
-
return wsQueryOnce(projectId, requestId, qp, eventModel, options, newToken, resolvedHost);
|
|
170
|
-
}
|
|
171
|
-
throw err;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
async function querySql(projectId, sql, hostUrl) {
|
|
175
|
-
const requestId = genRequestId("sqlIde");
|
|
176
|
-
const qp = {
|
|
177
|
-
events: { sql },
|
|
178
|
-
eventView: { sqlViewParams: [] }
|
|
179
|
-
};
|
|
180
|
-
return wsQuery(projectId, requestId, qp, 10, {
|
|
181
|
-
searchSource: "sqlIde",
|
|
182
|
-
querySource: "sqlIde"
|
|
183
|
-
}, hostUrl);
|
|
184
|
-
}
|
|
185
|
-
async function queryReportData(projectId, reportId, qp, eventModel, options = {}, hostUrl) {
|
|
186
|
-
const dashboardId = options.dashboardId || 0;
|
|
187
|
-
const requestId = genRequestId(`${projectId}_${dashboardId}_${reportId}`);
|
|
188
|
-
return wsQuery(projectId, requestId, qp, eventModel, {
|
|
189
|
-
searchSource: options.searchSource || "model_search",
|
|
190
|
-
querySource: options.querySource || "module",
|
|
191
|
-
...options
|
|
192
|
-
}, hostUrl);
|
|
193
|
-
}
|
|
194
|
-
export {
|
|
195
|
-
httpDelete,
|
|
196
|
-
httpGet,
|
|
197
|
-
httpPost,
|
|
198
|
-
httpRequest,
|
|
199
|
-
httpUpload,
|
|
200
|
-
queryReportData,
|
|
201
|
-
querySql,
|
|
202
|
-
wsQuery
|
|
203
|
-
};
|