@linxin666/dsh-client-ui-task-board 0.3.10 → 0.3.12

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
@@ -369,7 +369,7 @@ function settleExecution(task, executionId, outcome, now, error) {
369
369
  };
370
370
  const executions = [...task.executions];
371
371
  executions[index] = settled;
372
- const status = outcome === "succeeded" ? "done" : outcome === "failed" ? "failed" : task.status === "running" ? "todo" : task.status;
372
+ const status = outcome === "succeeded" ? task.schedule?.enabled ? "todo" : "done" : outcome === "failed" ? "failed" : task.status === "running" ? "todo" : task.status;
373
373
  return {
374
374
  ...task,
375
375
  status,
@@ -2013,6 +2013,10 @@ function isServiceUnavailable(error) {
2013
2013
  const code = error.code;
2014
2014
  return code === "service-unavailable" || code === "gateway/service-unavailable";
2015
2015
  }
2016
+ function isInvocationUnavailable(error) {
2017
+ const code = error.code;
2018
+ return code === "invocation-unavailable" || code === "gateway/invocation-unavailable";
2019
+ }
2016
2020
  const SERVICE_UNAVAILABLE_ATTEMPTS = 5;
2017
2021
  const SERVICE_UNAVAILABLE_BACKOFF_MS = 2e3;
2018
2022
  function delay(ms) {
@@ -2093,6 +2097,7 @@ var HostExecutionRunner = class {
2093
2097
  scanMemos = /* @__PURE__ */ new Map();
2094
2098
  unavailableAttempts;
2095
2099
  unavailableBackoffMs;
2100
+ unsupportedSessionListWarned = false;
2096
2101
  constructor(gateway, commands, workspaceRegistry, unavailableRetry) {
2097
2102
  this.gateway = gateway;
2098
2103
  this.commands = commands;
@@ -2167,6 +2172,13 @@ var HostExecutionRunner = class {
2167
2172
  items: response.items
2168
2173
  };
2169
2174
  } catch (error) {
2175
+ if (isInvocationUnavailable(error)) {
2176
+ if (!this.unsupportedSessionListWarned) {
2177
+ this.unsupportedSessionListWarned = true;
2178
+ console.warn("[dsh-task-board] DSH runtime session endpoint unavailable (requires DSH >= 0.1.2-alpha.2); task board roster auto-discovery is disabled", error);
2179
+ }
2180
+ return { known: false };
2181
+ }
2170
2182
  if (!isServiceUnavailable(error) || attempt >= this.unavailableAttempts) {
2171
2183
  console.error("[dsh-task-board] session/list failed; treating the host session roster as unknown", error);
2172
2184
  return { known: false };
@@ -2183,6 +2195,13 @@ var HostExecutionRunner = class {
2183
2195
  try {
2184
2196
  response = await this.invoke("session", "list", {});
2185
2197
  } catch (error) {
2198
+ if (isInvocationUnavailable(error)) {
2199
+ if (!this.unsupportedSessionListWarned) {
2200
+ this.unsupportedSessionListWarned = true;
2201
+ console.warn("[dsh-task-board] DSH runtime session endpoint unavailable (requires DSH >= 0.1.2-alpha.2); task board roster auto-discovery is disabled", error);
2202
+ }
2203
+ return { outcome: "pending" };
2204
+ }
2186
2205
  console.warn("[dsh-task-board] session/list failed during execution inspection; keeping the outcome pending", error);
2187
2206
  return { outcome: "pending" };
2188
2207
  }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Async-boundary guard shared by the plugin family's Host halves: every
3
+ * fire-and-forget promise chain and callback the host runtime does not own
4
+ * (route handlers, timers, event listeners, spawned work) funnels through
5
+ * these helpers. The dsh host installs a process-level fail-loud guard that
6
+ * turns ANY unhandled promise rejection into a whole-process exit — one
7
+ * plugin's stray rejection would otherwise take every plugin down. These
8
+ * helpers exist so that failure mode is structurally impossible in family
9
+ * code: the rejection becomes a logged error at the plugin boundary instead.
10
+ *
11
+ * Complements the aggregate's shell isolation (packages/dsh-web-all): the
12
+ * shell contains import/activation failures at boot; runGuarded contains
13
+ * run-time failures after activation.
14
+ * @module dsh-web-shared/host/run-guarded
15
+ */
16
+ /**
17
+ * Run one async operation, logging (never propagating) a rejection. Use for
18
+ * promises whose lifecycle the caller does not await: spawned work, retry
19
+ * loops, background flushes. Returns the original promise so callers can
20
+ * still chain when they want to.
21
+ * @param promise - the work to shield; any thenable.
22
+ * @param label - log prefix naming the work site.
23
+ * @param log - error sink; defaults to console.error.
24
+ * @returns the input promise (rejection already consumed).
25
+ */
26
+ export declare function runGuarded<T>(promise: PromiseLike<T>, label: string, log?: (error: unknown) => void): PromiseLike<T>;
27
+ /**
28
+ * Wrap one callback so every invocation is individually guarded: a rejection
29
+ * inside one call is logged and swallowed instead of escaping into whatever
30
+ * infrastructure invoked the callback (HTTP server, EventEmitter, interval).
31
+ * Sync throws are caught identically; a returned promise is replaced by
32
+ * `undefined` after guarding (callers that need the original rejection should
33
+ * await inside their own try/catch instead).
34
+ * @param label - log prefix naming the callback site.
35
+ * @param handler - the work to guard.
36
+ * @param log - error sink; defaults to console.error.
37
+ * @returns a wrapped callback with the same parameter list.
38
+ */
39
+ export declare function guardedHandler<A extends unknown[]>(label: string, handler: (...args: A) => unknown, log?: (error: unknown) => void): (...args: A) => unknown;
@@ -58,6 +58,7 @@ export declare class HostExecutionRunner {
58
58
  private readonly scanMemos;
59
59
  private readonly unavailableAttempts;
60
60
  private readonly unavailableBackoffMs;
61
+ private unsupportedSessionListWarned;
61
62
  constructor(gateway: SessionGateway | TypertGateway, commands?: SessionCommandDispatcher | undefined, workspaceRegistry?: TaskBoardWorkspaceRegistry | undefined, unavailableRetry?: {
62
63
  attempts?: number;
63
64
  backoffMs?: number;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@linxin666/dsh-client-ui-task-board",
3
3
  "description": "Host-authoritative task board for the DSH Web GUI with real session execution, Host cron scheduling, and optional cross-platform idle-sleep protection; mounted without DSH source changes.",
4
- "version": "0.3.10",
4
+ "version": "0.3.12",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "dsh": {
25
25
  "engines": {
26
- "dsh": ">=0.1.2-alpha.1"
26
+ "dsh": ">=0.1.2-alpha.4"
27
27
  },
28
28
  "bundle": {
29
29
  "patch": "./cordis.patch.yml"
@@ -48,23 +48,23 @@
48
48
  },
49
49
  "devDependencies": {
50
50
  "@deepseek-ai/cordis": "^4.0.2",
51
- "@deepseek-ai/dsh-agent": "^0.1.2-alpha.2",
52
- "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.2",
53
- "@deepseek-ai/dsh-api-gateway": "^0.1.2-alpha.2",
54
- "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.2",
55
- "@deepseek-ai/dsh-workspace": "^0.1.2-alpha.2",
56
- "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.2",
57
- "@deepseek-ai/dsh-api-workspace-controller": "^0.1.2-alpha.2",
58
- "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.2",
59
- "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.2",
60
- "@deepseek-ai/dsh-client-store": "^0.1.2-alpha.2",
61
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.2",
62
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.2",
63
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.2-alpha.2",
64
- "@deepseek-ai/dsh-commands": "^0.1.2-alpha.2",
65
- "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.2",
66
- "@deepseek-ai/dsh-settings": "^0.1.2-alpha.2",
67
- "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.2",
51
+ "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4",
52
+ "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.4",
53
+ "@deepseek-ai/dsh-api-gateway": "^0.1.2-alpha.4",
54
+ "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.4",
55
+ "@deepseek-ai/dsh-workspace": "^0.1.2-alpha.4",
56
+ "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.4",
57
+ "@deepseek-ai/dsh-api-workspace-controller": "^0.1.2-alpha.4",
58
+ "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.4",
59
+ "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.4",
60
+ "@deepseek-ai/dsh-client-store": "^0.1.2-alpha.4",
61
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.4",
62
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.4",
63
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.2-alpha.4",
64
+ "@deepseek-ai/dsh-commands": "^0.1.2-alpha.4",
65
+ "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.4",
66
+ "@deepseek-ai/dsh-settings": "^0.1.2-alpha.4",
67
+ "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4",
68
68
  "@types/node": "^22.20.0",
69
69
  "@types/react": "~18.3.1",
70
70
  "@types/react-dom": "^18.3.5",
@@ -99,7 +99,8 @@ html[data-dsh-taskboard-active]:not([data-dsh-ssh-active]) [class*='centerCol']
99
99
  }
100
100
 
101
101
  /* Collapsed rail: icon-only, centered, matching the shell's 56px rail. */
102
- [data-dsh-frame][data-sidebar-collapsed] .entry {
102
+ :global([data-dsh-frame][data-sidebar-collapsed]) .entry,
103
+ :global([data-sidebar-collapsed]) .entry {
103
104
  justify-content: center;
104
105
  padding: 0;
105
106
  width: 36px;
@@ -108,7 +109,8 @@ html[data-dsh-taskboard-active]:not([data-dsh-ssh-active]) [class*='centerCol']
108
109
  border-radius: 50%;
109
110
  }
110
111
 
111
- [data-dsh-frame][data-sidebar-collapsed] .entryLabel {
112
+ :global([data-dsh-frame][data-sidebar-collapsed]) .entryLabel,
113
+ :global([data-sidebar-collapsed]) .entryLabel {
112
114
  display: none;
113
115
  }
114
116
 
@@ -207,7 +207,7 @@ export function apply(ctx: ClientContext): void {
207
207
  transport: new HttpTaskBoardHostTransport(),
208
208
  sessions: {
209
209
  list: sessions.list,
210
- open: id => sessions.open(id as SessionId),
210
+ open: id => sessions.open(id as never),
211
211
  },
212
212
  })
213
213
  controller.start()
package/src/core/tasks.ts CHANGED
@@ -361,7 +361,8 @@ export function settleExecution(
361
361
  const settled: ExecutionRecord = { ...execution, endedAt: now, result: outcome, error }
362
362
  const executions = [...task.executions]
363
363
  executions[index] = settled
364
- const status: TaskStatus = outcome === 'succeeded' ? 'done'
364
+ const status: TaskStatus = outcome === 'succeeded'
365
+ ? (task.schedule?.enabled ? 'todo' : 'done')
365
366
  : outcome === 'failed' ? 'failed'
366
367
  : task.status === 'running' ? 'todo' : task.status
367
368
  return { ...task, status, updatedAt: now, executions }
@@ -0,0 +1,76 @@
1
+ // Generated by scripts/sync-shared.mjs from shared/host/run-guarded.ts. Do not edit this copy; edit the shared source and run "node scripts/sync-shared.mjs".
2
+ /**
3
+ * Async-boundary guard shared by the plugin family's Host halves: every
4
+ * fire-and-forget promise chain and callback the host runtime does not own
5
+ * (route handlers, timers, event listeners, spawned work) funnels through
6
+ * these helpers. The dsh host installs a process-level fail-loud guard that
7
+ * turns ANY unhandled promise rejection into a whole-process exit — one
8
+ * plugin's stray rejection would otherwise take every plugin down. These
9
+ * helpers exist so that failure mode is structurally impossible in family
10
+ * code: the rejection becomes a logged error at the plugin boundary instead.
11
+ *
12
+ * Complements the aggregate's shell isolation (packages/dsh-web-all): the
13
+ * shell contains import/activation failures at boot; runGuarded contains
14
+ * run-time failures after activation.
15
+ * @module dsh-web-shared/host/run-guarded
16
+ */
17
+
18
+ /** Format one failure line for logging. */
19
+ function formatFailure(label: string, error: unknown): Error {
20
+ return new Error(`[${label}] unhandled async failure: ${error instanceof Error ? error.stack ?? error.message : String(error)}`)
21
+ }
22
+
23
+ /**
24
+ * Run one async operation, logging (never propagating) a rejection. Use for
25
+ * promises whose lifecycle the caller does not await: spawned work, retry
26
+ * loops, background flushes. Returns the original promise so callers can
27
+ * still chain when they want to.
28
+ * @param promise - the work to shield; any thenable.
29
+ * @param label - log prefix naming the work site.
30
+ * @param log - error sink; defaults to console.error.
31
+ * @returns the input promise (rejection already consumed).
32
+ */
33
+ export function runGuarded<T>(promise: PromiseLike<T>, label: string, log: (error: unknown) => void = console.error): PromiseLike<T> {
34
+ void Promise.resolve(promise).catch(error => {
35
+ log(formatFailure(label, error))
36
+ })
37
+ return promise
38
+ }
39
+
40
+ /**
41
+ * Wrap one callback so every invocation is individually guarded: a rejection
42
+ * inside one call is logged and swallowed instead of escaping into whatever
43
+ * infrastructure invoked the callback (HTTP server, EventEmitter, interval).
44
+ * Sync throws are caught identically; a returned promise is replaced by
45
+ * `undefined` after guarding (callers that need the original rejection should
46
+ * await inside their own try/catch instead).
47
+ * @param label - log prefix naming the callback site.
48
+ * @param handler - the work to guard.
49
+ * @param log - error sink; defaults to console.error.
50
+ * @returns a wrapped callback with the same parameter list.
51
+ */
52
+ export function guardedHandler<A extends unknown[]>(
53
+ label: string,
54
+ handler: (...args: A) => unknown,
55
+ log: (error: unknown) => void = console.error,
56
+ ): (...args: A) => unknown {
57
+ return (...args: A) => {
58
+ try {
59
+ const result = handler(...args)
60
+ if (isPromiseLike(result)) {
61
+ void Promise.resolve(result).catch(error => {
62
+ log(formatFailure(label, error))
63
+ })
64
+ return undefined
65
+ }
66
+ return result
67
+ } catch (error) {
68
+ log(formatFailure(label, error))
69
+ return undefined
70
+ }
71
+ }
72
+ }
73
+
74
+ function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
75
+ return typeof value === 'object' && value !== null && typeof (value as PromiseLike<unknown>).then === 'function'
76
+ }
@@ -40,6 +40,11 @@ function isServiceUnavailable(error: unknown): boolean {
40
40
  return code === 'service-unavailable' || code === 'gateway/service-unavailable'
41
41
  }
42
42
 
43
+ function isInvocationUnavailable(error: unknown): boolean {
44
+ const code = (error as { code?: unknown }).code
45
+ return code === 'invocation-unavailable' || code === 'gateway/invocation-unavailable'
46
+ }
47
+
43
48
  const SERVICE_UNAVAILABLE_ATTEMPTS = 5
44
49
  const SERVICE_UNAVAILABLE_BACKOFF_MS = 2_000
45
50
 
@@ -145,6 +150,7 @@ export class HostExecutionRunner {
145
150
  private readonly scanMemos = new Map<string, number>()
146
151
  private readonly unavailableAttempts: number
147
152
  private readonly unavailableBackoffMs: number
153
+ private unsupportedSessionListWarned = false
148
154
 
149
155
  constructor(
150
156
  private readonly gateway: SessionGateway | TypertGateway,
@@ -214,6 +220,13 @@ export class HostExecutionRunner {
214
220
  const response = await this.invoke('session', 'list', {}) as SessionListValue
215
221
  return { known: true, count: response.items.filter(item => item.running).length, items: response.items as SessionSummary[] }
216
222
  } catch (error) {
223
+ if (isInvocationUnavailable(error)) {
224
+ if (!this.unsupportedSessionListWarned) {
225
+ this.unsupportedSessionListWarned = true
226
+ console.warn('[dsh-task-board] DSH runtime session endpoint unavailable (requires DSH >= 0.1.2-alpha.2); task board roster auto-discovery is disabled', error)
227
+ }
228
+ return { known: false }
229
+ }
217
230
  if (!isServiceUnavailable(error) || attempt >= this.unavailableAttempts) {
218
231
  console.error('[dsh-task-board] session/list failed; treating the host session roster as unknown', error)
219
232
  return { known: false }
@@ -233,6 +246,13 @@ export class HostExecutionRunner {
233
246
  try {
234
247
  response = await this.invoke('session', 'list', {}) as SessionListValue
235
248
  } catch (error) {
249
+ if (isInvocationUnavailable(error)) {
250
+ if (!this.unsupportedSessionListWarned) {
251
+ this.unsupportedSessionListWarned = true
252
+ console.warn('[dsh-task-board] DSH runtime session endpoint unavailable (requires DSH >= 0.1.2-alpha.2); task board roster auto-discovery is disabled', error)
253
+ }
254
+ return { outcome: 'pending' }
255
+ }
236
256
  console.warn('[dsh-task-board] session/list failed during execution inspection; keeping the outcome pending', error)
237
257
  return { outcome: 'pending' }
238
258
  }