oasis_test_v2 2.2.106 → 2.2.108
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/index.js +173 -15
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -43276,7 +43276,7 @@ var require_websocket = __commonJS({
|
|
|
43276
43276
|
var http2 = require("http");
|
|
43277
43277
|
var net = require("net");
|
|
43278
43278
|
var tls = require("tls");
|
|
43279
|
-
var { randomBytes: randomBytes17, createHash:
|
|
43279
|
+
var { randomBytes: randomBytes17, createHash: createHash47 } = require("crypto");
|
|
43280
43280
|
var { Duplex, Readable } = require("stream");
|
|
43281
43281
|
var { URL: URL2 } = require("url");
|
|
43282
43282
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -43944,7 +43944,7 @@ var require_websocket = __commonJS({
|
|
|
43944
43944
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
43945
43945
|
return;
|
|
43946
43946
|
}
|
|
43947
|
-
const digest =
|
|
43947
|
+
const digest = createHash47("sha1").update(key2 + GUID).digest("base64");
|
|
43948
43948
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
43949
43949
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
43950
43950
|
return;
|
|
@@ -44313,7 +44313,7 @@ var require_websocket_server = __commonJS({
|
|
|
44313
44313
|
var EventEmitter = require("events");
|
|
44314
44314
|
var http2 = require("http");
|
|
44315
44315
|
var { Duplex } = require("stream");
|
|
44316
|
-
var { createHash:
|
|
44316
|
+
var { createHash: createHash47 } = require("crypto");
|
|
44317
44317
|
var extension3 = require_extension();
|
|
44318
44318
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
44319
44319
|
var subprotocol2 = require_subprotocol();
|
|
@@ -44620,7 +44620,7 @@ var require_websocket_server = __commonJS({
|
|
|
44620
44620
|
);
|
|
44621
44621
|
}
|
|
44622
44622
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
44623
|
-
const digest =
|
|
44623
|
+
const digest = createHash47("sha1").update(key2 + GUID).digest("base64");
|
|
44624
44624
|
const headers = [
|
|
44625
44625
|
"HTTP/1.1 101 Switching Protocols",
|
|
44626
44626
|
"Upgrade: websocket",
|
|
@@ -206747,6 +206747,35 @@ var init_host_isolation = __esm({
|
|
|
206747
206747
|
});
|
|
206748
206748
|
|
|
206749
206749
|
// ../connectors/src/feishu/index.ts
|
|
206750
|
+
async function fetchFeishuTenantAccessToken(appId, appSecret) {
|
|
206751
|
+
try {
|
|
206752
|
+
const resp = await fetch(FEISHU_TENANT_TOKEN_URL, {
|
|
206753
|
+
method: "POST",
|
|
206754
|
+
headers: { "Content-Type": "application/json; charset=utf-8" },
|
|
206755
|
+
body: JSON.stringify({ app_id: appId, app_secret: appSecret }),
|
|
206756
|
+
signal: AbortSignal.timeout(TENANT_TOKEN_TIMEOUT_MS)
|
|
206757
|
+
});
|
|
206758
|
+
const data = await resp.json();
|
|
206759
|
+
if (!resp.ok || data.code !== 0 || typeof data.tenant_access_token !== "string" || !data.tenant_access_token) {
|
|
206760
|
+
log("[feishu]", `\u6362\u4F01\u4E1A token \u5931\u8D25\uFF08\u672C\u8F6E bot \u8EAB\u4EFD\u4E0D\u53EF\u7528\uFF09: HTTP ${resp.status} code=${String(data.code)} ${String(data.msg ?? "")}`.trim());
|
|
206761
|
+
return null;
|
|
206762
|
+
}
|
|
206763
|
+
return data.tenant_access_token;
|
|
206764
|
+
} catch (err) {
|
|
206765
|
+
log("[feishu]", `\u6362\u4F01\u4E1A token \u5931\u8D25\uFF08\u672C\u8F6E bot \u8EAB\u4EFD\u4E0D\u53EF\u7528\uFF09: ${String(err?.message ?? err).slice(0, 200)}`);
|
|
206766
|
+
return null;
|
|
206767
|
+
}
|
|
206768
|
+
}
|
|
206769
|
+
function previousTenantToken(credsFile, appId) {
|
|
206770
|
+
if (!credsFile) return null;
|
|
206771
|
+
try {
|
|
206772
|
+
const prev = readSessionCredsFile(credsFile);
|
|
206773
|
+
if (prev["LARKSUITE_CLI_APP_ID"] !== appId) return null;
|
|
206774
|
+
return prev["LARKSUITE_CLI_TENANT_ACCESS_TOKEN"] || null;
|
|
206775
|
+
} catch {
|
|
206776
|
+
return null;
|
|
206777
|
+
}
|
|
206778
|
+
}
|
|
206750
206779
|
function parseFeishuTokenResponse(data, nowMs) {
|
|
206751
206780
|
const accessToken = String(data["access_token"] ?? "");
|
|
206752
206781
|
const refreshToken = String(data["refresh_token"] ?? "");
|
|
@@ -206765,7 +206794,7 @@ function parseFeishuTokenResponse(data, nowMs) {
|
|
|
206765
206794
|
expiresAt: new Date(expiresAtMs)
|
|
206766
206795
|
};
|
|
206767
206796
|
}
|
|
206768
|
-
var import_node_child_process14, import_node_util4, import_node_path16, import_node_url3, import_promises9, execFileAsync2, __dirname2, FEISHU_WELL_KNOWN_SKILLS_BASE, FEISHU_TOKEN_URL, FeishuConnector;
|
|
206797
|
+
var import_node_child_process14, import_node_util4, import_node_path16, import_node_url3, import_promises9, execFileAsync2, __dirname2, FEISHU_WELL_KNOWN_SKILLS_BASE, FEISHU_TOKEN_URL, FEISHU_TENANT_TOKEN_URL, TENANT_TOKEN_TIMEOUT_MS, FeishuConnector;
|
|
206769
206798
|
var init_feishu = __esm({
|
|
206770
206799
|
"../connectors/src/feishu/index.ts"() {
|
|
206771
206800
|
"use strict";
|
|
@@ -206775,10 +206804,13 @@ var init_feishu = __esm({
|
|
|
206775
206804
|
import_node_url3 = require("node:url");
|
|
206776
206805
|
import_promises9 = require("node:fs/promises");
|
|
206777
206806
|
init_base();
|
|
206807
|
+
init_logger();
|
|
206778
206808
|
execFileAsync2 = (0, import_node_util4.promisify)(import_node_child_process14.execFile);
|
|
206779
206809
|
__dirname2 = (0, import_node_path16.dirname)((0, import_node_url3.fileURLToPath)(__esm_import_meta_url));
|
|
206780
206810
|
FEISHU_WELL_KNOWN_SKILLS_BASE = "https://open.feishu.cn/.well-known/skills";
|
|
206781
206811
|
FEISHU_TOKEN_URL = "https://open.feishu.cn/open-apis/authen/v2/oauth/token";
|
|
206812
|
+
FEISHU_TENANT_TOKEN_URL = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal";
|
|
206813
|
+
TENANT_TOKEN_TIMEOUT_MS = 5e3;
|
|
206782
206814
|
FeishuConnector = class extends CliConnector {
|
|
206783
206815
|
config = {
|
|
206784
206816
|
slug: "feishu",
|
|
@@ -206876,9 +206908,12 @@ var init_feishu = __esm({
|
|
|
206876
206908
|
/** 厂商技能包源——平台自动拉取飞书官方技能包时用。 */
|
|
206877
206909
|
wellKnownSkillsBase = FEISHU_WELL_KNOWN_SKILLS_BASE;
|
|
206878
206910
|
larkBin;
|
|
206879
|
-
|
|
206911
|
+
fetchTenantToken;
|
|
206912
|
+
/** `fetchTenantToken` 只给测试替换(不打真飞书);生产一律用 {@link fetchFeishuTenantAccessToken}。 */
|
|
206913
|
+
constructor(larkBin, opts = {}) {
|
|
206880
206914
|
super(__dirname2);
|
|
206881
206915
|
this.larkBin = larkBin ?? this.resolveLarkBin();
|
|
206916
|
+
this.fetchTenantToken = opts.fetchTenantToken ?? fetchFeishuTenantAccessToken;
|
|
206882
206917
|
}
|
|
206883
206918
|
/**
|
|
206884
206919
|
* 解析 user_info 的 JSON。lark-cli 的 `ok:false` 也是**退出码 0** 的正常输出,
|
|
@@ -206909,6 +206944,7 @@ var init_feishu = __esm({
|
|
|
206909
206944
|
/**
|
|
206910
206945
|
* Agent run 开始:把凭据作为 lark-cli「外部凭据」写进凭据文件(不进 agent env)。
|
|
206911
206946
|
* credentials 接受:app_id, app_secret, user_access_token?(及其 FEISHU_* 别名)。
|
|
206947
|
+
* 企业 token 不随 job 下发,由这里用 app_id/app_secret 现换(换不到只是本轮没有 bot 身份,不抛)。
|
|
206912
206948
|
*
|
|
206913
206949
|
* 注入的 sessionEnv 键(全部非敏感):
|
|
206914
206950
|
* FEISHU_CREDS_FILE — 凭据文件路径,wrapper source 它
|
|
@@ -206921,9 +206957,11 @@ var init_feishu = __esm({
|
|
|
206921
206957
|
if (!appId) throw new Error("feishu inject: missing app_id");
|
|
206922
206958
|
if (!appSecret) throw new Error("feishu inject: missing app_secret");
|
|
206923
206959
|
const userAccessToken = credentials2["user_access_token"] ?? credentials2["FEISHU_USER_ACCESS_TOKEN"];
|
|
206960
|
+
const tenantAccessToken = this.isSimulated() ? null : await this.fetchTenantToken(appId, appSecret) ?? previousTenantToken(sessionEnv.get("FEISHU_CREDS_FILE"), appId);
|
|
206924
206961
|
const creds = await writeSessionCredsFile(this.config.slug, {
|
|
206925
206962
|
LARKSUITE_CLI_APP_ID: appId,
|
|
206926
206963
|
LARKSUITE_CLI_APP_SECRET: appSecret,
|
|
206964
|
+
...tenantAccessToken ? { LARKSUITE_CLI_TENANT_ACCESS_TOKEN: tenantAccessToken } : {},
|
|
206927
206965
|
...userAccessToken ? { LARKSUITE_CLI_USER_ACCESS_TOKEN: userAccessToken } : {}
|
|
206928
206966
|
}, sessionEnv.get("FEISHU_RUN_TMPDIR"));
|
|
206929
206967
|
const configDir = (0, import_node_path16.join)(creds.dir, "lark-config");
|
|
@@ -210646,7 +210684,7 @@ async function prepareConnectorsForJob(job, deps) {
|
|
|
210646
210684
|
await connector.inject(credentials2, sessionEnv);
|
|
210647
210685
|
for (const [k2, v2] of sessionEnv) envOverrides[k2] = v2;
|
|
210648
210686
|
cleanups.push(() => connector.cleanup(sessionEnv));
|
|
210649
|
-
injected.set(slug5, { connector, sessionEnv, credsFingerprint: credentialsFingerprint(credentials2) });
|
|
210687
|
+
injected.set(slug5, { connector, sessionEnv, credentials: credentials2, credsFingerprint: credentialsFingerprint(credentials2) });
|
|
210650
210688
|
status.credsReady = true;
|
|
210651
210689
|
} catch (err) {
|
|
210652
210690
|
status.error = String(err);
|
|
@@ -210772,7 +210810,12 @@ async function renewInjectedCredentials(injected, fetchCredentials) {
|
|
|
210772
210810
|
try {
|
|
210773
210811
|
fresh = await fetchCredentials();
|
|
210774
210812
|
} catch (err) {
|
|
210775
|
-
for (const slug5 of injected
|
|
210813
|
+
for (const [slug5, target] of injected) {
|
|
210814
|
+
outcome.failed.push({ slug: slug5, error: String(err) });
|
|
210815
|
+
await target.connector.inject(target.credentials, target.sessionEnv).catch((reErr) => {
|
|
210816
|
+
log("[node-connectors]", `\u26A0 \u7EED\u671F\uFF1A${slug5} \u6309\u65E7\u51ED\u636E\u91CD\u6CE8\u5931\u8D25: ${String(reErr)}`);
|
|
210817
|
+
});
|
|
210818
|
+
}
|
|
210776
210819
|
log("[node-connectors]", `\u7EED\u671F\uFF1A\u5411 server \u53D6\u51ED\u636E\u5931\u8D25\uFF0C\u672C\u8F6E\u6CBF\u7528\u65E7\u51ED\u636E: ${String(err)}`);
|
|
210777
210820
|
return outcome;
|
|
210778
210821
|
}
|
|
@@ -210781,6 +210824,7 @@ async function renewInjectedCredentials(injected, fetchCredentials) {
|
|
|
210781
210824
|
if (!target) continue;
|
|
210782
210825
|
try {
|
|
210783
210826
|
await target.connector.inject(credentials2, target.sessionEnv);
|
|
210827
|
+
target.credentials = credentials2;
|
|
210784
210828
|
const fingerprint2 = credentialsFingerprint(credentials2);
|
|
210785
210829
|
if (fingerprint2 !== target.credsFingerprint) {
|
|
210786
210830
|
target.credsFingerprint = fingerprint2;
|
|
@@ -220270,6 +220314,9 @@ var init_memory_control_plane_store = __esm({
|
|
|
220270
220314
|
/** protected:FileControlPlaneStore 子类 open() 回放快照时直接恢复(密码须持久化)。 */
|
|
220271
220315
|
accountSecrets = /* @__PURE__ */ new Map();
|
|
220272
220316
|
// accountId → { passwordHash?, tokenVersion }
|
|
220317
|
+
/** 已登出会话(ADR 0636;protected:文件版子类回放快照时直接恢复——不持久化的话重启即「复活」)。 */
|
|
220318
|
+
revokedSessions = /* @__PURE__ */ new Map();
|
|
220319
|
+
// tokenHash → record
|
|
220273
220320
|
verificationCodes = /* @__PURE__ */ new Map();
|
|
220274
220321
|
// email → record(短 TTL,文件版不持久化)
|
|
220275
220322
|
/** 组织模型三表(protected:文件版子类回放快照时直接恢复)。 */
|
|
@@ -220631,6 +220678,15 @@ var init_memory_control_plane_store = __esm({
|
|
|
220631
220678
|
this.accountSecrets.set(accountId, { ...cur, tokenVersion: next });
|
|
220632
220679
|
return next;
|
|
220633
220680
|
}
|
|
220681
|
+
// —— 已登出会话(ADR 0636)——
|
|
220682
|
+
async revokeSession(rec2) {
|
|
220683
|
+
const cutoff = Date.parse(rec2.revokedAt);
|
|
220684
|
+
for (const [k2, r] of this.revokedSessions) if (Date.parse(r.expiresAt) < cutoff) this.revokedSessions.delete(k2);
|
|
220685
|
+
if (!this.revokedSessions.has(rec2.tokenHash)) this.revokedSessions.set(rec2.tokenHash, { ...rec2 });
|
|
220686
|
+
}
|
|
220687
|
+
async isSessionRevoked(tokenHash) {
|
|
220688
|
+
return this.revokedSessions.has(tokenHash);
|
|
220689
|
+
}
|
|
220634
220690
|
// —— 邮箱验证码(单邮箱一条 active)——
|
|
220635
220691
|
async putVerificationCode(rec2) {
|
|
220636
220692
|
this.verificationCodes.set(rec2.email, { ...rec2 });
|
|
@@ -225833,6 +225889,7 @@ var init_control_plane_file_store = __esm({
|
|
|
225833
225889
|
"upsertAccount",
|
|
225834
225890
|
"setAccountPassword",
|
|
225835
225891
|
"bumpAccountTokenVersion",
|
|
225892
|
+
"revokeSession",
|
|
225836
225893
|
"upsertDigitalEmployee",
|
|
225837
225894
|
"patchDigitalEmployee",
|
|
225838
225895
|
"putAgentCredential",
|
|
@@ -225872,6 +225929,7 @@ var init_control_plane_file_store = __esm({
|
|
|
225872
225929
|
for (const link of snap.inviteLinks ?? []) await MemoryControlPlaneStore.prototype.createInviteLink.call(store, link);
|
|
225873
225930
|
for (const a of snap.accounts ?? []) await MemoryControlPlaneStore.prototype.upsertAccount.call(store, a);
|
|
225874
225931
|
for (const e of snap.accountSecrets ?? []) store.accountSecrets.set(e.accountId, { ...e.secret });
|
|
225932
|
+
for (const r of snap.revokedSessions ?? []) store.revokedSessions.set(r.tokenHash, { ...r });
|
|
225875
225933
|
for (const e of snap.digitalEmployees ?? []) await MemoryControlPlaneStore.prototype.upsertDigitalEmployee.call(store, e);
|
|
225876
225934
|
for (const c of snap.agentCredentials ?? []) await MemoryControlPlaneStore.prototype.putAgentCredential.call(store, c);
|
|
225877
225935
|
for (const b2 of snap.principalBridges ?? []) await MemoryControlPlaneStore.prototype.putPrincipalBridge.call(store, b2);
|
|
@@ -225910,6 +225968,8 @@ var init_control_plane_file_store = __esm({
|
|
|
225910
225968
|
inviteLinks: [...this.inviteLinks.values()],
|
|
225911
225969
|
accounts,
|
|
225912
225970
|
accountSecrets,
|
|
225971
|
+
// 只有按哈希查的读面,直接读 protected map(同 offboardings)。
|
|
225972
|
+
revokedSessions: [...this.revokedSessions.values()],
|
|
225913
225973
|
digitalEmployees,
|
|
225914
225974
|
agentCredentials,
|
|
225915
225975
|
principalBridges,
|
|
@@ -230576,6 +230636,12 @@ function verifySession(token2, secret, nowMs) {
|
|
|
230576
230636
|
if (typeof claims.exp !== "number" || claims.exp <= nowMs) return null;
|
|
230577
230637
|
return claims;
|
|
230578
230638
|
}
|
|
230639
|
+
function sessionTokenHash(token2) {
|
|
230640
|
+
return (0, import_node_crypto54.createHash)("sha256").update(token2).digest("base64url");
|
|
230641
|
+
}
|
|
230642
|
+
function newSessionId() {
|
|
230643
|
+
return (0, import_node_crypto54.randomBytes)(16).toString("base64url");
|
|
230644
|
+
}
|
|
230579
230645
|
function generateCode() {
|
|
230580
230646
|
return String((0, import_node_crypto54.randomInt)(0, 1e6)).padStart(6, "0");
|
|
230581
230647
|
}
|
|
@@ -246652,7 +246718,7 @@ function createAuth(opts) {
|
|
|
246652
246718
|
const passwordFailures = /* @__PURE__ */ new Map();
|
|
246653
246719
|
const sessionCookie = (accountId, tokenVersion) => {
|
|
246654
246720
|
const t = now();
|
|
246655
|
-
const jwt = signSession({ sub: accountId, tokenVersion, iat: t, exp: t + sessionTtlMs }, jwtSecret);
|
|
246721
|
+
const jwt = signSession({ sub: accountId, tokenVersion, iat: t, exp: t + sessionTtlMs, jti: newSessionId() }, jwtSecret);
|
|
246656
246722
|
const attrs = [`${cookieName}=${jwt}`, "Path=/", "HttpOnly", "SameSite=Lax", `Max-Age=${Math.floor(sessionTtlMs / 1e3)}`];
|
|
246657
246723
|
if (opts.secureCookie) attrs.push("Secure");
|
|
246658
246724
|
return attrs.join("; ");
|
|
@@ -246710,11 +246776,13 @@ function createAuth(opts) {
|
|
|
246710
246776
|
await store.deleteVerificationCode(key2);
|
|
246711
246777
|
return "ok";
|
|
246712
246778
|
};
|
|
246779
|
+
const sessionToken = (headers) => parseCookies(typeof headers.cookie === "string" ? headers.cookie : void 0)[cookieName];
|
|
246713
246780
|
const currentAccount = async (headers) => {
|
|
246714
|
-
const raw =
|
|
246781
|
+
const raw = sessionToken(headers);
|
|
246715
246782
|
if (!raw) return null;
|
|
246716
246783
|
const claims = verifySession(raw, jwtSecret, now());
|
|
246717
246784
|
if (!claims) return null;
|
|
246785
|
+
if (await store.isSessionRevoked(sessionTokenHash(raw))) return null;
|
|
246718
246786
|
const sec = await store.getAccountSecret(claims.sub);
|
|
246719
246787
|
if (!sec || sec.tokenVersion !== claims.tokenVersion) return null;
|
|
246720
246788
|
const account = await store.getAccount(claims.sub);
|
|
@@ -246891,6 +246959,21 @@ function createAuth(opts) {
|
|
|
246891
246959
|
return { status: 200, body: { ticket, expiresAt: new Date(exp).toISOString() } };
|
|
246892
246960
|
}
|
|
246893
246961
|
if (route2 === "logout") {
|
|
246962
|
+
const raw = sessionToken(headers);
|
|
246963
|
+
const claims = raw ? verifySession(raw, jwtSecret, now()) : null;
|
|
246964
|
+
if (raw && claims) {
|
|
246965
|
+
try {
|
|
246966
|
+
await store.revokeSession({
|
|
246967
|
+
tokenHash: sessionTokenHash(raw),
|
|
246968
|
+
accountId: claims.sub,
|
|
246969
|
+
revokedAt: new Date(now()).toISOString(),
|
|
246970
|
+
expiresAt: new Date(claims.exp).toISOString()
|
|
246971
|
+
});
|
|
246972
|
+
} catch (e) {
|
|
246973
|
+
console.error(`[auth] \u767B\u51FA\u540A\u9500\u5931\u8D25 ${claims.sub}: ${e instanceof Error ? e.message : String(e)}`);
|
|
246974
|
+
return { status: 500, body: { error: { code: "LOGOUT_REVOKE_FAILED", message: "\u767B\u51FA\u672A\u5B8C\u6210\uFF0C\u8BF7\u91CD\u8BD5" } }, setCookie: clearCookie() };
|
|
246975
|
+
}
|
|
246976
|
+
}
|
|
246894
246977
|
return { status: 200, body: { ok: true }, setCookie: clearCookie() };
|
|
246895
246978
|
}
|
|
246896
246979
|
return { status: 404, body: { error: { code: "NOT_FOUND", message: pathname } } };
|
|
@@ -264917,6 +265000,14 @@ var init_postgres_control_plane = __esm({
|
|
|
264917
265000
|
expires_at timestamptz NOT NULL,
|
|
264918
265001
|
attempts int NOT NULL DEFAULT 0
|
|
264919
265002
|
)`);
|
|
265003
|
+
await pool.query(`
|
|
265004
|
+
CREATE TABLE IF NOT EXISTS "${s2}".revoked_sessions (
|
|
265005
|
+
token_hash text PRIMARY KEY, -- SHA-256(base64url) of the oasis_auth JWT
|
|
265006
|
+
account_id text NOT NULL,
|
|
265007
|
+
revoked_at timestamptz NOT NULL,
|
|
265008
|
+
expires_at timestamptz NOT NULL -- = \u4EE4\u724C exp\uFF1B\u8FC7\u4E86\u4EE4\u724C\u672C\u6765\u5C31\u9A8C\u4E0D\u8FC7\uFF0C\u884C\u53EF\u5220
|
|
265009
|
+
)`);
|
|
265010
|
+
await pool.query(`CREATE INDEX IF NOT EXISTS revoked_sessions_expires_idx ON "${s2}".revoked_sessions (expires_at)`);
|
|
264920
265011
|
await pool.query(`ALTER TABLE "${s2}".company_members ADD COLUMN IF NOT EXISTS display_name text`);
|
|
264921
265012
|
await pool.query(`ALTER TABLE "${s2}".company_members ADD COLUMN IF NOT EXISTS created_at timestamptz`);
|
|
264922
265013
|
await pool.query(`ALTER TABLE "${s2}".company_members ADD COLUMN IF NOT EXISTS updated_at timestamptz`);
|
|
@@ -265807,6 +265898,19 @@ var init_postgres_control_plane = __esm({
|
|
|
265807
265898
|
if (r.rowCount === 0) throw new Error(`account not found: ${accountId}`);
|
|
265808
265899
|
return Number(r.rows[0].token_version);
|
|
265809
265900
|
}
|
|
265901
|
+
// —— 已登出会话(ADR 0636)——
|
|
265902
|
+
async revokeSession(rec2) {
|
|
265903
|
+
await this.pool.query(`DELETE FROM ${this.s}.revoked_sessions WHERE expires_at < $1`, [rec2.revokedAt]);
|
|
265904
|
+
await this.pool.query(
|
|
265905
|
+
`INSERT INTO ${this.s}.revoked_sessions (token_hash, account_id, revoked_at, expires_at) VALUES ($1,$2,$3,$4)
|
|
265906
|
+
ON CONFLICT (token_hash) DO NOTHING`,
|
|
265907
|
+
[rec2.tokenHash, rec2.accountId, rec2.revokedAt, rec2.expiresAt]
|
|
265908
|
+
);
|
|
265909
|
+
}
|
|
265910
|
+
async isSessionRevoked(tokenHash) {
|
|
265911
|
+
const r = await this.pool.query(`SELECT 1 FROM ${this.s}.revoked_sessions WHERE token_hash = $1`, [tokenHash]);
|
|
265912
|
+
return (r.rowCount ?? 0) > 0;
|
|
265913
|
+
}
|
|
265810
265914
|
// —— 邮箱验证码(单邮箱一条 active)——
|
|
265811
265915
|
async putVerificationCode(rec2) {
|
|
265812
265916
|
await this.pool.query(
|
|
@@ -298359,12 +298463,13 @@ var COMMAND_DECLS = {
|
|
|
298359
298463
|
// 填(agent 调 = 它绑定的那个真人),命令行不收。开关 OASIS_PERSONAL_TODO=off 时服务端 404。 ——
|
|
298360
298464
|
todo: {
|
|
298361
298465
|
group: "\u4E2A\u4EBA\u5F85\u529E",
|
|
298362
|
-
usage: "oasis todo <add|list|edit|done|cancel|reopen> ...",
|
|
298466
|
+
usage: "oasis todo <add|list|show|edit|done|cancel|reopen> ...",
|
|
298363
298467
|
description: "\u4E2A\u4EBA\u5F85\u529E\uFF1A\u66FF\u4F60\u7ED1\u5B9A\u7684\u90A3\u4E2A\u771F\u4EBA\u8BB0\u4E0B\u6709\u622A\u6B62\u65E5\u7684\u4E8B\uFF0C\u5230\u671F\u524D\u4E00\u5929 09:00\u3001\u5F53\u5929 09:00 \u7CFB\u7EDF\u4F1A\u63D0\u9192 TA\uFF08\u8BBE\u4E86\u5F00\u59CB\u65E5\u7684\uFF0C\u5F00\u59CB\u5F53\u5929 09:00 \u4E5F\u63D0\u9192\u4E00\u6B21\uFF09\u3002\u6BCF\u6761\u8F93\u51FA\u7B2C\u4E00\u884C\u662F\u300C\u4ECA\u5929\uFF1AYYYY-MM-DD \u5468X\uFF08\u65F6\u533A\uFF09\u300D\u2014\u2014\u6362\u7B97\u300C\u8FD9\u5468\u4E94\u300D\u300C\u6708\u5E95\u300D\u4E00\u5F8B\u4EE5\u5B83\u4E3A\u51C6\u3002**\u6CA1\u6709 `oasis todo` \u547D\u4EE4\u6267\u884C\u6210\u529F\uFF0C\u5C31\u4E0D\u80FD\u8BF4\u8BB0\u4E0B\u4E86\u3002**",
|
|
298364
|
-
subCommands: ["todo add", "todo list", "todo edit", "todo done", "todo cancel", "todo reopen"],
|
|
298468
|
+
subCommands: ["todo add", "todo list", "todo show", "todo edit", "todo done", "todo cancel", "todo reopen"],
|
|
298365
298469
|
examples: [
|
|
298366
298470
|
'oasis todo add --title "\u4EA4 Q3 \u590D\u76D8\u62A5\u544A" --note "\u738B\u603B\u8981\uFF0C\u5468\u4F1A\u524D\u8FC7\u4E00\u904D" --due 2026-10-09',
|
|
298367
298471
|
"oasis todo list",
|
|
298472
|
+
"oasis todo show td_3f9a1c2b",
|
|
298368
298473
|
'oasis todo edit td_3f9a1c2b --due 2026-10-12 --reason "\u6392\u671F\u8C03\u6574"',
|
|
298369
298474
|
"oasis todo done td_3f9a1c2b"
|
|
298370
298475
|
]
|
|
@@ -298389,6 +298494,13 @@ var COMMAND_DECLS = {
|
|
|
298389
298494
|
description: "\u5217\u51FA\u5F85\u529E\uFF0C\u6309\u622A\u6B62\u65E5\u6392\u5E8F\uFF0C\u8FC7\u671F\u7684\u6807\u300C\u5DF2\u8FC7\u671F\u300D\uFF1B\u8BBE\u4E86\u5F00\u59CB\u65E5\u7684\u663E\u793A\u300CX \u6708 X \u65E5\u5F00\u59CB \u2192 X \u6708 X \u65E5\u622A\u6B62\u300D\u3002\u9ED8\u8BA4\u53EA\u5217\u672A\u5B8C\u6210\u7684\uFF1B--all \u8FDE\u5DF2\u5B8C\u6210\u3001\u5DF2\u53D6\u6D88\u7684\u4E00\u8D77\u5217\u3002",
|
|
298390
298495
|
flags: [{ name: "all", desc: "\u8FDE\u5DF2\u5B8C\u6210 / \u5DF2\u53D6\u6D88\u7684\u4E00\u8D77\u5217", boolean: true, own: true }]
|
|
298391
298496
|
},
|
|
298497
|
+
"todo show": {
|
|
298498
|
+
group: "\u4E2A\u4EBA\u5F85\u529E",
|
|
298499
|
+
usage: "oasis todo show <id>",
|
|
298500
|
+
description: "\u770B\u4E00\u6761\u5F85\u529E\u7684\u5168\u8C8C\uFF1A\u6807\u9898\u3001\u5F00\u59CB \u2192 \u622A\u6B62\u3001\u72B6\u6001\u3001\u5907\u6CE8\u5168\u6587\uFF0C\u4EE5\u53CA\u53D8\u66F4\u8BB0\u5F55\uFF08\u65F6\u95F4\u3001\u8C01\u6539\u7684 / \u7ECF\u624B agent\u3001\u5B57\u6BB5\u3001\u65E7\u503C \u2192 \u65B0\u503C\u3001\u539F\u56E0\uFF0C\u65B0\u7684\u5728\u524D\uFF09\u3002\u6539\u5907\u6CE8\u524D\u5148\u7528\u5B83\u8BFB\u51FA\u5F53\u524D\u5907\u6CE8\u5168\u6587\uFF1B\u7528\u6237\u95EE\u300C\u4E4B\u524D\u5199\u7684\u662F\u4EC0\u4E48\u300D\u4E5F\u67E5\u8FD9\u91CC\u3002",
|
|
298501
|
+
positional: [{ name: "<id>", required: true, desc: "\u5F85\u529E id\uFF08oasis todo list \u7B2C\u4E00\u5217\uFF09" }],
|
|
298502
|
+
examples: ["oasis todo show td_3f9a1c2b"]
|
|
298503
|
+
},
|
|
298392
298504
|
"todo edit": {
|
|
298393
298505
|
group: "\u4E2A\u4EBA\u5F85\u529E",
|
|
298394
298506
|
usage: "oasis todo edit <id> [--title \u2026] [--note \u2026] [--start YYYY-MM-DD|none] [--start-time HH:mm|none] [--due YYYY-MM-DD] [--time HH:mm|none] [--workorder <\u5DE5\u5355 id>|none] [--reason \u2026]",
|
|
@@ -299279,7 +299391,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
|
|
|
299279
299391
|
}
|
|
299280
299392
|
}
|
|
299281
299393
|
}
|
|
299282
|
-
var CLI_VERSION = "2.2.
|
|
299394
|
+
var CLI_VERSION = "2.2.108" ? "2.2.108" : "dev";
|
|
299283
299395
|
function apiHeaders(token2, company) {
|
|
299284
299396
|
const headers = {
|
|
299285
299397
|
authorization: `Bearer ${token2}`,
|
|
@@ -301711,6 +301823,52 @@ ${res.warning}`);
|
|
|
301711
301823
|
}
|
|
301712
301824
|
break;
|
|
301713
301825
|
}
|
|
301826
|
+
if (sub === "show") {
|
|
301827
|
+
const id = idOf();
|
|
301828
|
+
const [one2, day] = await Promise.all([
|
|
301829
|
+
call("GET", todoPath(id)),
|
|
301830
|
+
call("GET", "/api/todos")
|
|
301831
|
+
]);
|
|
301832
|
+
const t = one2.todo;
|
|
301833
|
+
const statusText = t.status === "done" ? "\u5DF2\u5B8C\u6210" : t.status === "cancelled" ? "\u5DF2\u53D6\u6D88" : t.dueDate < day.today.date ? "\u672A\u5B8C\u6210\uFF08\u5DF2\u8FC7\u671F\uFF09" : "\u672A\u5B8C\u6210";
|
|
301834
|
+
println(day.todayLine);
|
|
301835
|
+
println(`${t.id} ${t.title}`);
|
|
301836
|
+
println(`\u65F6\u95F4\uFF1A${due(t)}`);
|
|
301837
|
+
println(`\u72B6\u6001\uFF1A${statusText}`);
|
|
301838
|
+
if (t.linkedWorkorderId) println(`\u5DE5\u5355\uFF1A${t.linkedWorkorderId}`);
|
|
301839
|
+
println("\u5907\u6CE8\uFF1A");
|
|
301840
|
+
println(t.note ? t.note : "\uFF08\u65E0\uFF09");
|
|
301841
|
+
const FIELD = {
|
|
301842
|
+
created: "\u8BB0\u4E0B",
|
|
301843
|
+
status: "\u72B6\u6001",
|
|
301844
|
+
title: "\u6807\u9898",
|
|
301845
|
+
note: "\u5907\u6CE8",
|
|
301846
|
+
startDate: "\u5F00\u59CB\u65E5",
|
|
301847
|
+
startTime: "\u5F00\u59CB\u65F6\u523B",
|
|
301848
|
+
dueDate: "\u622A\u6B62\u65E5",
|
|
301849
|
+
dueTime: "\u622A\u6B62\u65F6\u523B",
|
|
301850
|
+
linkedWorkorderId: "\u5DE5\u5355"
|
|
301851
|
+
};
|
|
301852
|
+
const STATUS = { open: "\u672A\u5B8C\u6210", done: "\u5DF2\u5B8C\u6210", cancelled: "\u5DF2\u53D6\u6D88" };
|
|
301853
|
+
const val = (field, v2) => v2 === void 0 || v2 === "" ? "\uFF08\u7A7A\uFF09" : field === "status" ? STATUS[v2] ?? v2 : v2;
|
|
301854
|
+
const fmtAt = (iso9) => new Intl.DateTimeFormat("sv-SE", {
|
|
301855
|
+
timeZone: day.today.timeZone,
|
|
301856
|
+
year: "numeric",
|
|
301857
|
+
month: "2-digit",
|
|
301858
|
+
day: "2-digit",
|
|
301859
|
+
hour: "2-digit",
|
|
301860
|
+
minute: "2-digit",
|
|
301861
|
+
hour12: false
|
|
301862
|
+
}).format(new Date(iso9));
|
|
301863
|
+
const changes = [...one2.changes].sort((a, b2) => b2.at.localeCompare(a.at));
|
|
301864
|
+
println(`\u53D8\u66F4\u8BB0\u5F55\uFF08${changes.length} \u6761\uFF0C\u65B0\u7684\u5728\u524D\uFF09\uFF1A`);
|
|
301865
|
+
for (const c of changes) {
|
|
301866
|
+
const who = c.handActorId ? `${c.actorId}\uFF08\u7ECF\u624B ${c.handActorId}\uFF09` : c.actorId;
|
|
301867
|
+
const what = c.field === "created" ? `\u8BB0\u4E0B\uFF1A${c.newValue ?? ""}` : `${FIELD[c.field] ?? c.field}\uFF1A${val(c.field, c.oldValue)} \u2192 ${val(c.field, c.newValue)}`;
|
|
301868
|
+
println(`- ${fmtAt(c.at)} ${who} ${what}${c.reason ? ` \u539F\u56E0\uFF1A${c.reason}` : ""}`);
|
|
301869
|
+
}
|
|
301870
|
+
break;
|
|
301871
|
+
}
|
|
301714
301872
|
if (sub === "edit") {
|
|
301715
301873
|
const id = idOf();
|
|
301716
301874
|
const body2 = {};
|
|
@@ -301736,7 +301894,7 @@ ${res.warning}`);
|
|
|
301736
301894
|
println(`${sub === "done" ? "\u5DF2\u5B8C\u6210" : sub === "cancel" ? "\u5DF2\u53D6\u6D88" : "\u5DF2\u91CD\u65B0\u6253\u5F00"}\uFF1A${r.todo.title}\uFF0C${due(r.todo)}`);
|
|
301737
301895
|
break;
|
|
301738
301896
|
}
|
|
301739
|
-
throw new Error("\u7528\u6CD5: oasis todo <add|list|edit|done|cancel|reopen> \u2026\uFF08oasis todo --help \u770B\u8BE6\u60C5\uFF09");
|
|
301897
|
+
throw new Error("\u7528\u6CD5: oasis todo <add|list|show|edit|done|cancel|reopen> \u2026\uFF08oasis todo --help \u770B\u8BE6\u60C5\uFF09");
|
|
301740
301898
|
}
|
|
301741
301899
|
case "skill": {
|
|
301742
301900
|
const sub = positional[0];
|
|
@@ -303404,7 +303562,7 @@ function shimScript() {
|
|
|
303404
303562
|
}
|
|
303405
303563
|
|
|
303406
303564
|
// src/index.ts
|
|
303407
|
-
var PKG_VERSION = true ? "2.2.
|
|
303565
|
+
var PKG_VERSION = true ? "2.2.108" : "dev";
|
|
303408
303566
|
var LOCAL_BIN = localBin();
|
|
303409
303567
|
var NPM_PREFIX = npmPrefix();
|
|
303410
303568
|
var INSTANCE = DEFAULT_INSTANCE;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oasis_test_v2",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.108",
|
|
4
4
|
"description": "Oasis node daemon + CLI — background daemon, auto-start, full server CLI",
|
|
5
5
|
"bin": {
|
|
6
6
|
"oasis": "./dist/index.js"
|
|
@@ -26,6 +26,6 @@
|
|
|
26
26
|
"node": ">=20"
|
|
27
27
|
},
|
|
28
28
|
"oasisRelease": {
|
|
29
|
-
"sourceHead": "
|
|
29
|
+
"sourceHead": "a953575106dcf944d7b32f0eca289792092539ff"
|
|
30
30
|
}
|
|
31
31
|
}
|