Skip to content

feat: group-scoped workdir + init/destroy lifecycle scripts - #1

Merged
ranxianglei merged 3 commits into
masterfrom
feat/daemon-workdir
Jul 28, 2026
Merged

feat: group-scoped workdir + init/destroy lifecycle scripts#1
ranxianglei merged 3 commits into
masterfrom
feat/daemon-workdir

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Part of the daemon workdir refactor (cross-repo: router + daemon + web).

  • server.ts reads the x-ework-group-config header (base64 JSON) and passes it to engine.handleEvent(event, groupConfig)
  • opencode.ts Engine stores the config keyed by issue; resolveWorkdir uses the template ({owner}/{repo}/{issue}/{session}) when present, falling back to the default baseWorkdir path
  • execProcess runs initScript in the workdir before each opencode spawn
  • handleClosed runs destroyScript on issue close, then clears the stored config
  • envInitScript is accepted but not executed (placeholder per spec)
  • Scripts run via bash -c with the workdir as cwd; failures are logged (warn) but never block the main flow
  • tsc clean, 81 tests pass

…scripts

Engine.handleEvent now accepts an optional GroupConfig (forwarded by the router via x-ework-group-config header, parsed in server.ts). When a workdirTemplate is present, resolveWorkdir substitutes {owner}/{repo}/{issue}/{session} and uses it instead of the default baseWorkdir path. initScript runs in the workdir before each opencode spawn (execProcess pre-spawn). destroyScript runs on issue close (handleClosed). envInitScript is accepted but not yet executed (placeholder). Scripts run via bash with the workdir as cwd; failures are logged but never block the main flow.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…header parsing

Extracts resolveTemplatedWorkdir and runHookScript as exported standalone functions (previously private Engine methods) and exports parseGroupConfigHeader from server.ts so they are unit-testable. Also tightens the owner/repo fallback to use || instead of ?? so empty-string scope parts correctly fall back to 'default'. 16 tests covering: all 4 template variables, scopeKey-part fallback, homedir expansion, unknown placeholders, base64 round-trip, malformed input rejection, script cwd, workdir auto-create, and never-throws-on-failure invariant.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Addresses CRITICAL+HIGH+MEDIUM findings from dual-agent review:

H2+H3 (HIGH): runHookScript switched from Bun.spawnSync (blocks event loop) to async Bun.spawn with a 60s hard timeout (SIGKILL). A hung initScript no longer freezes the entire daemon (heartbeats, stdout draining, other webhooks).

H4 (HIGH): handleClosed no longer calls resolveWorkdir (which triggers git clone on empty dirs). New workdirPathFor computes the path without side effects; destroyScript only runs on workdirs that already exist (existsSync check).

M1 (MEDIUM): TOCTOU on groupConfigs Map — handleClosed now uses compare-and-delete (only deletes if the map value is still the same gc reference, so a concurrent handleEvent.set isn't clobbered).

M2 (MEDIUM): handleClosed early-returns if issue.state === 'closed' — duplicate close webhooks (Gitea redelivers) no longer re-run destroyScript.

M3 (MEDIUM): destroyScript dedupes workdirs via Set before looping — a template without {session} no longer runs destroy N times on the same dir.

M5 (MEDIUM): hookEnvFor injects EWORK_OWNER/EWORK_REPO/EWORK_ISSUE/EWORK_SESSION/EWORK_WORKDIR into the script environment. The web UI placeholder now shows real env var names instead of the non-existent ${CLONE_URL}.

L4 (LOW): resolveTemplatedWorkdir uses replacer functions (() => value) instead of raw strings — prevents $&/$1 interpretation in String.replace.

L5 (LOW): relative templates are now anchored to baseWorkdir (matching RecloneStrategy behavior). The baseWorkdir param is threaded from resolveWorkdir.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@ranxianglei
ranxianglei merged commit 7874989 into master Jul 28, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants