@michengai/dsh-automation 0.1.26 → 0.1.28

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/lib/index.js CHANGED
@@ -15,15 +15,42 @@ import { setApprovalPolicy } from "@deepseek-ai/dsh-user-approval";
15
15
  import { WorkspaceId } from "@deepseek-ai/dsh-workspace";
16
16
 
17
17
  // src/run-title.ts
18
- function formatRunStamp(iso) {
18
+ var RUN_STAMP_OPTIONS = {
19
+ year: "numeric",
20
+ month: "2-digit",
21
+ day: "2-digit",
22
+ hour: "2-digit",
23
+ minute: "2-digit",
24
+ hourCycle: "h23"
25
+ };
26
+ var zonedRunStampFormatters = /* @__PURE__ */ new Map();
27
+ function runStampFormatter(timeZone2) {
28
+ const cached2 = zonedRunStampFormatters.get(timeZone2);
29
+ if (cached2 !== void 0) return cached2;
30
+ const formatter = new Intl.DateTimeFormat("en-CA", { ...RUN_STAMP_OPTIONS, timeZone: timeZone2 });
31
+ zonedRunStampFormatters.set(timeZone2, formatter);
32
+ return formatter;
33
+ }
34
+ function formatRunStamp(iso, timeZone2) {
19
35
  const value = new Date(iso);
20
36
  if (Number.isNaN(value.getTime())) return iso;
21
- const date5 = `${value.getFullYear()}-${String(value.getMonth() + 1).padStart(2, "0")}-${String(value.getDate()).padStart(2, "0")}`;
22
- const time3 = `${String(value.getHours()).padStart(2, "0")}:${String(value.getMinutes()).padStart(2, "0")}`;
23
- return `${date5} ${time3}`;
37
+ if (timeZone2 === void 0) {
38
+ const date5 = `${value.getFullYear()}-${String(value.getMonth() + 1).padStart(2, "0")}-${String(value.getDate()).padStart(2, "0")}`;
39
+ const time3 = `${String(value.getHours()).padStart(2, "0")}:${String(value.getMinutes()).padStart(2, "0")}`;
40
+ return `${date5} ${time3}`;
41
+ }
42
+ try {
43
+ const parts = runStampFormatter(timeZone2).formatToParts(value);
44
+ const part = (type) => parts.find((item) => item.type === type)?.value;
45
+ const [year, month, day, hour, minute] = ["year", "month", "day", "hour", "minute"].map((type) => part(type));
46
+ if ([year, month, day, hour, minute].some((item) => item === void 0)) return iso;
47
+ return `${year}-${month}-${day} ${hour}:${minute}`;
48
+ } catch {
49
+ return iso;
50
+ }
24
51
  }
25
- function automationSessionTitle(taskName, iso) {
26
- return `${formatRunStamp(iso)} - ${taskName}`;
52
+ function automationSessionTitle(taskName, iso, timeZone2) {
53
+ return `${formatRunStamp(iso, timeZone2)} - ${taskName}`;
27
54
  }
28
55
 
29
56
  // src/executor.ts
@@ -151,7 +178,11 @@ async function executeAutomationRun(ctx, definition, run, config2) {
151
178
  }));
152
179
  await handle.agent.whenIdle();
153
180
  await workspace.attachSession(sessionId);
154
- pinAutomationSessionTitle(ctx, handle.agent.session, automationSessionTitle(definition.name, run.startedAt ?? run.scheduledFor));
181
+ pinAutomationSessionTitle(ctx, handle.agent.session, automationSessionTitle(
182
+ definition.name,
183
+ run.startedAt ?? run.scheduledFor,
184
+ definition.timeZone
185
+ ));
155
186
  const firstSeq = handle.agent.session.seq;
156
187
  handle.agent.followup(createUserMessage({
157
188
  content: [{ type: "text", text: run.promptSnapshot }],
@@ -273,7 +304,7 @@ var AUTOMATION_CREATE_DESCRIPTION = [
273
304
  "\u4E3A\u5F53\u524D\u5DE5\u4F5C\u533A\u521B\u5EFA\u4E00\u6761\u72EC\u7ACB DSH \u81EA\u52A8\u5316\uFF1B\u6BCF\u6B21\u89E6\u53D1\u5F00\u542F\u5168\u65B0 Session\uFF0C\u4E0D\u7EE7\u627F\u5F53\u524D\u5BF9\u8BDD\u3002",
274
305
  '\u5DE5\u4F5C\u65E5\u65E9\u4E0A 8 \u70B9\u793A\u4F8B\uFF1Akind=weekly, weekdays=["MO","TU","WE","TH","FR"], time="08:00", time_zone="Asia/Shanghai"\u3002',
275
306
  "\u540C\u65F6\u652F\u6301 hourly(minute)\u3001monthly(month_day + time) \u548C custom(every_days + time)\u3002",
276
- "\u5FC5\u987B\u4F7F\u7528\u663E\u5F0F IANA \u65F6\u533A\u3002\u6700\u77ED\u95F4\u9694 5 \u5206\u949F\u3002\u9ED8\u8BA4\u53EA\u8BFB\uFF1B\u53EA\u6709\u9700\u8981\u6539\u6587\u4EF6\u65F6\u624D\u9009 workspace-write\u3002"
307
+ "\u5FC5\u987B\u4F7F\u7528\u663E\u5F0F IANA \u65F6\u533A\u3002\u6700\u77ED\u95F4\u9694 5 \u5206\u949F\u3002\u9ED8\u8BA4\u6743\u9650\u6765\u81EA Host\uFF1B\u4E0D\u9700\u8981\u5199\u5165\u65F6\u5E94\u663E\u5F0F\u9009\u62E9 read-only\uFF0C\u53EA\u6709\u9700\u8981\u6539\u6587\u4EF6\u65F6\u624D\u9009 workspace-write\u3002"
277
308
  ].join("");
278
309
 
279
310
  // src/service.ts
@@ -21364,6 +21395,29 @@ function normalizeSchedule(schedule) {
21364
21395
  const selected = new Set(schedule.weekdays);
21365
21396
  return { ...schedule, weekdays: WEEKDAY_ORDER.filter((day) => selected.has(day)) };
21366
21397
  }
21398
+ function isEqualSchedule(left, right) {
21399
+ const normalizedLeft = normalizeSchedule(left);
21400
+ const normalizedRight = normalizeSchedule(right);
21401
+ if (normalizedLeft.kind !== normalizedRight.kind || normalizedLeft.timeZone !== normalizedRight.timeZone) {
21402
+ return false;
21403
+ }
21404
+ switch (normalizedLeft.kind) {
21405
+ case "once":
21406
+ return normalizedRight.kind === "once" && normalizedLeft.at === normalizedRight.at;
21407
+ case "interval":
21408
+ return normalizedRight.kind === "interval" && normalizedLeft.everyMinutes === normalizedRight.everyMinutes && normalizedLeft.anchor === normalizedRight.anchor;
21409
+ case "hourly":
21410
+ return normalizedRight.kind === "hourly" && normalizedLeft.minute === normalizedRight.minute;
21411
+ case "daily":
21412
+ return normalizedRight.kind === "daily" && normalizedLeft.time === normalizedRight.time;
21413
+ case "weekly":
21414
+ return normalizedRight.kind === "weekly" && normalizedLeft.time === normalizedRight.time && normalizedLeft.weekdays.length === normalizedRight.weekdays.length && normalizedLeft.weekdays.every((day, index) => day === normalizedRight.weekdays[index]);
21415
+ case "monthly":
21416
+ return normalizedRight.kind === "monthly" && normalizedLeft.day === normalizedRight.day && normalizedLeft.time === normalizedRight.time;
21417
+ case "custom":
21418
+ return normalizedRight.kind === "custom" && normalizedLeft.everyDays === normalizedRight.everyDays && normalizedLeft.time === normalizedRight.time;
21419
+ }
21420
+ }
21367
21421
  function scheduleToRRule(schedule) {
21368
21422
  const normalized = normalizeSchedule(schedule);
21369
21423
  if (normalized.kind === "once") {
@@ -21968,7 +22022,7 @@ var AutomationService = class _AutomationService {
21968
22022
  normalizedFields = { ...normalizedFields, workspaceId: target.id, cwd: target.path };
21969
22023
  }
21970
22024
  try {
21971
- const scheduleChanged = fields.schedule !== void 0 && JSON.stringify(normalizeSchedule(fields.schedule)) !== JSON.stringify(current.schedule);
22025
+ const scheduleChanged = fields.schedule !== void 0 && !isEqualSchedule(fields.schedule, current.schedule);
21972
22026
  if (scheduleChanged && fields.schedule?.kind === "once" && nextOccurrence(fields.schedule, now2) === null) {
21973
22027
  throw new AutomationRequestError("\u4E00\u6B21\u6027\u81EA\u52A8\u5316\u5FC5\u987B\u5B89\u6392\u5728\u672A\u6765\u65F6\u95F4\u3002");
21974
22028
  }
@@ -1,5 +1,8 @@
1
1
  import type { ClientRpc } from './contracts.js';
2
2
  import type { AutomationSnapshot, CreateAutomationInput, MutateRequest } from './protocol.js';
3
+ export declare function snapshotPollIntervalMs(runs: readonly {
4
+ readonly status: string;
5
+ }[] | undefined): number;
3
6
  export declare function isTransportError(error: unknown): boolean;
4
7
  export interface AutomationClientState {
5
8
  readonly phase: 'idle' | 'loading' | 'ready' | 'error';
@@ -43,6 +43,7 @@ export declare function sessionUpdatedAtIso(value: number | string | undefined,
43
43
  export declare function groupScheduledSessions(automations: readonly {
44
44
  readonly id: string;
45
45
  readonly name: string;
46
+ readonly timeZone?: string;
46
47
  }[], runs: readonly ScheduleRunLike[]): ScheduleRailGroup[];
47
48
  export interface OverviewAutomationLike {
48
49
  readonly id: string;
@@ -59,8 +60,12 @@ export interface TaskOverviewRow {
59
60
  }
60
61
  /** 任务总览:每个定义一行;最近一次留有会话的运行决定该行是否可点开。 */
61
62
  export declare function deriveTaskOverviewRows(automations: readonly OverviewAutomationLike[], runs: readonly ScheduleRunLike[]): TaskOverviewRow[];
62
- /** 归档中的、以及宿主已经不认识的 Session,都不应再出现在定时页。 */
63
- export declare function keepScheduledSessionLink(sessionId: string | undefined, archived: ReadonlySet<string>, presentIds?: ReadonlySet<string>): boolean;
63
+ /** 归档立即摘掉。宿主会话簿经常晚于自动化快照,缺席不能当成已删除。 */
64
+ export declare function keepScheduledSessionLink(sessionId: string | undefined, archived: ReadonlySet<string>, _presentIds?: ReadonlySet<string>): boolean;
65
+ /** 当前打开的是定时会话,但快照还没有这条执行记录时,侧栏应立刻再拉一次。 */
66
+ export declare function scheduledSessionNeedsSnapshotRefresh(sessionId: string | null | undefined, runs: readonly {
67
+ readonly sessionId?: string;
68
+ }[] | undefined): boolean;
64
69
  export declare function collectScheduledSessionIds(runs: readonly {
65
70
  readonly sessionId?: string | null;
66
71
  }[] | undefined): Set<string>;
@@ -1,6 +1,8 @@
1
1
  import type { AutomationSchedule } from './types.ts';
2
2
  export declare function assertValidSchedule(schedule: AutomationSchedule): void;
3
3
  export declare function normalizeSchedule(schedule: AutomationSchedule): AutomationSchedule;
4
+ /** 按计划字段比较语义,避免对象字段顺序影响变更判断。 */
5
+ export declare function isEqualSchedule(left: AutomationSchedule, right: AutomationSchedule): boolean;
4
6
  export declare function scheduleToRRule(schedule: AutomationSchedule): string;
5
7
  export declare function nextOccurrence(schedule: AutomationSchedule, afterExclusive: string): string | null;
6
8
  export declare function latestDueOccurrence(schedule: AutomationSchedule, now: string): string | null;
@@ -1,3 +1,3 @@
1
1
  /** 定时执行 Session 的展示标题,Host 与侧栏共用。 */
2
- export declare function formatRunStamp(iso: string): string;
3
- export declare function automationSessionTitle(taskName: string, iso: string): string;
2
+ export declare function formatRunStamp(iso: string, timeZone?: string): string;
3
+ export declare function automationSessionTitle(taskName: string, iso: string, timeZone?: string): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@michengai/dsh-automation",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "description": "在独立 DSH Session 中按计划执行编码任务,支持 Web 设置页与 Agent 双入口管理。",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",