@cydm/happy-elves 0.1.0-beta.294 → 0.1.0-beta.296
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/apps/cli/dist/commands/lib/usage.js +8 -2
- package/apps/cli/dist/commands/release.js +42 -1
- package/apps/cli/dist/commands/session.js +14 -1
- package/apps/cli/package.json +1 -1
- package/apps/daemon/package.json +1 -1
- package/package.json +1 -1
- package/packages/client/dist/client.js +2 -0
- package/packages/client/dist/types.d.ts +2 -0
|
@@ -123,12 +123,15 @@ session stars, recent sessions, pinned workspaces, and current project defaults.
|
|
|
123
123
|
happy-elves release profile show [--profile <name>] --json
|
|
124
124
|
happy-elves release profile set <name> --release-tag <tag> [--release-tag <tag>] [--reviewer-tag <tag>] [--dogfood-tag <tag>] [--no-daemon-update] --json
|
|
125
125
|
happy-elves release resolve [--profile happy-elves] --json
|
|
126
|
+
happy-elves release notify [--profile happy-elves] [--text 发布] [--detach] [--timeout 1000s] --json
|
|
126
127
|
happy-elves release start [--profile happy-elves] [--expected-commit <sha>] [--target-machine <machineId>] [--no-daemon-update] [--wait] --json
|
|
127
128
|
happy-elves release status [jobId] [--machine <machineId>] --json
|
|
128
129
|
happy-elves release logs [jobId] [--machine <machineId>] [--tail 200] --json
|
|
129
130
|
|
|
130
131
|
Release profiles resolve target sessions by encrypted session tags. The built-in
|
|
131
132
|
happy-elves profile uses Happy Elves + Release Session for the release target.
|
|
133
|
+
Release notify sends the short release prompt to that target with approve-all
|
|
134
|
+
permissions and memory off, so agents do not have to reconstruct release flags.
|
|
132
135
|
Release start runs a fixed daemon-local runner; it does not accept arbitrary shell.
|
|
133
136
|
`,
|
|
134
137
|
gateway: `Usage:
|
|
@@ -258,6 +261,7 @@ Core commands:
|
|
|
258
261
|
workspace pin-cwd --machine <machineId> --cwd <cwd> --json
|
|
259
262
|
|
|
260
263
|
release resolve --profile happy-elves --json
|
|
264
|
+
release notify --profile happy-elves --json
|
|
261
265
|
release start --profile happy-elves --json
|
|
262
266
|
|
|
263
267
|
gateway status --json
|
|
@@ -320,9 +324,11 @@ Release / reviewer / dogfood workflow:
|
|
|
320
324
|
session tag add <sessionId> --tag "Release Session" --json
|
|
321
325
|
2. Resolve before acting:
|
|
322
326
|
release resolve --profile happy-elves --json
|
|
323
|
-
3.
|
|
327
|
+
3. Notify release with the standard short prompt and release permissions:
|
|
328
|
+
release notify --profile happy-elves --json
|
|
329
|
+
4. Start the structured release runner only when explicitly needed:
|
|
324
330
|
release start --profile happy-elves --json
|
|
325
|
-
|
|
331
|
+
5. Use hidden sessions for temporary dogfood:
|
|
326
332
|
session create --hidden --source dogfood --tag "Happy Elves" --tag "Dogfood" ...
|
|
327
333
|
|
|
328
334
|
Concepts:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { normalizeSessionTags } from "../../../../packages/shared/dist/index.js";
|
|
4
|
-
import { CliError, ControllerClient, compactText, ok, parseDurationMs, readConfig, releaseProfilesPath, requirePositional, wantsJson, } from "./lib/index.js";
|
|
4
|
+
import { CliError, ControllerClient, DEFAULT_COMMAND_TIMEOUT_MS, compactText, ok, parseDurationMs, readConfig, releaseProfilesPath, requirePositional, wantsJson, } from "./lib/index.js";
|
|
5
5
|
const defaultReleaseProfile = {
|
|
6
6
|
name: "happy-elves",
|
|
7
7
|
releaseSessionTags: ["Happy Elves", "Release Session"],
|
|
@@ -81,6 +81,33 @@ export async function handleRelease({ domain, action, positional, flags }) {
|
|
|
81
81
|
}
|
|
82
82
|
return true;
|
|
83
83
|
}
|
|
84
|
+
if (action === "notify") {
|
|
85
|
+
const config = await readConfig(flags);
|
|
86
|
+
const client = new ControllerClient(config);
|
|
87
|
+
const { profile } = await loadProfile(profileNameFromFlags(flags));
|
|
88
|
+
const releaseTarget = await resolveRequiredReleaseTarget(client, profile);
|
|
89
|
+
const text = releaseNotifyText(flags);
|
|
90
|
+
const wait = flags.detach !== true && flags["no-wait"] !== true;
|
|
91
|
+
const result = await client.run(releaseTarget.sessionId, text, {
|
|
92
|
+
wait,
|
|
93
|
+
permissionMode: "approve-all",
|
|
94
|
+
memoryMode: "off",
|
|
95
|
+
memoryProjectKey: `${releaseTarget.machineId}:${releaseTarget.cwd}`,
|
|
96
|
+
waitTimeoutMs: parseDurationMs(flags.timeout, DEFAULT_COMMAND_TIMEOUT_MS),
|
|
97
|
+
ackTimeoutMs: parseDurationMs(flags["ack-timeout"], 15_000),
|
|
98
|
+
inactivityTimeoutMs: parseDurationMs(flags["inactivity-timeout"] ?? flags["runtime-timeout"], DEFAULT_COMMAND_TIMEOUT_MS),
|
|
99
|
+
});
|
|
100
|
+
printReleaseOutput("release.notify", {
|
|
101
|
+
profile: profile.name,
|
|
102
|
+
releaseTarget,
|
|
103
|
+
prompt: text,
|
|
104
|
+
permissionMode: "approve-all",
|
|
105
|
+
memoryMode: "off",
|
|
106
|
+
wait,
|
|
107
|
+
...result,
|
|
108
|
+
}, flags);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
84
111
|
if (action === "status" || action === "logs") {
|
|
85
112
|
const jobId = positional[0];
|
|
86
113
|
const config = await readConfig(flags);
|
|
@@ -101,6 +128,10 @@ export async function handleRelease({ domain, action, positional, flags }) {
|
|
|
101
128
|
function profileNameFromFlags(flags) {
|
|
102
129
|
return typeof flags.profile === "string" && flags.profile.trim() ? flags.profile.trim() : defaultReleaseProfile.name;
|
|
103
130
|
}
|
|
131
|
+
function releaseNotifyText(flags) {
|
|
132
|
+
const value = typeof flags.text === "string" ? flags.text.trim() : "";
|
|
133
|
+
return value || "发布";
|
|
134
|
+
}
|
|
104
135
|
function profileFromFlags(name, flags) {
|
|
105
136
|
const releaseSessionTags = tagsFromFlags(flags, "release-tag", "release-tags");
|
|
106
137
|
if (releaseSessionTags.length === 0)
|
|
@@ -212,6 +243,16 @@ async function resolveTaggedSession(client, tags) {
|
|
|
212
243
|
return { kind: "ambiguous", candidates: summaries };
|
|
213
244
|
return { kind: "found", session: summaries[0] };
|
|
214
245
|
}
|
|
246
|
+
async function resolveRequiredReleaseTarget(client, profile) {
|
|
247
|
+
const release = await resolveTaggedSession(client, profile.releaseSessionTags);
|
|
248
|
+
if (release.kind === "missing") {
|
|
249
|
+
throw new CliError(`No release session matches tags: ${profile.releaseSessionTags.join(", ")}`, "TAG_TARGET_NOT_FOUND");
|
|
250
|
+
}
|
|
251
|
+
if (release.kind === "ambiguous") {
|
|
252
|
+
throw new CliError(`Multiple release sessions match tags: ${profile.releaseSessionTags.join(", ")}`, "TAG_TARGET_AMBIGUOUS");
|
|
253
|
+
}
|
|
254
|
+
return release.session;
|
|
255
|
+
}
|
|
215
256
|
function emptyReleaseTarget() {
|
|
216
257
|
return { sessionId: "", machineId: "", agent: "", cwd: "", name: "", tags: [] };
|
|
217
258
|
}
|
|
@@ -203,6 +203,17 @@ function metadataTags(metadata) {
|
|
|
203
203
|
function isRecord(value) {
|
|
204
204
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
205
205
|
}
|
|
206
|
+
function sessionHiddenFromDefaultList(metadata) {
|
|
207
|
+
return metadata?.hiddenFromWorkspace === true ||
|
|
208
|
+
metadata?.visibility === "hidden" ||
|
|
209
|
+
metadata?.source === "dogfood";
|
|
210
|
+
}
|
|
211
|
+
async function defaultListIncludesSession(client, session) {
|
|
212
|
+
if (session.status === "closed")
|
|
213
|
+
return false;
|
|
214
|
+
const metadata = await client.decodeSessionMetadata(session);
|
|
215
|
+
return !sessionHiddenFromDefaultList(metadata);
|
|
216
|
+
}
|
|
206
217
|
function diagnosticSessionMetadata(metadata) {
|
|
207
218
|
const runtime = isRecord(metadata.runtime) ? metadata.runtime : {};
|
|
208
219
|
return {
|
|
@@ -428,7 +439,9 @@ export async function handleSession({ domain, action, positional, flags }) {
|
|
|
428
439
|
name: typeof flags.name === "string" ? flags.name : undefined,
|
|
429
440
|
...(tags.length > 0 ? { tags } : {}),
|
|
430
441
|
});
|
|
431
|
-
const visibleSessions = flags.all === true
|
|
442
|
+
const visibleSessions = flags.all === true
|
|
443
|
+
? allSessions
|
|
444
|
+
: (await Promise.all(allSessions.map(async (session) => (await defaultListIncludesSession(client, session) ? session : undefined)))).filter((session) => Boolean(session));
|
|
432
445
|
const { items: sessions, page } = selectWindow(visibleSessions, flags, 20);
|
|
433
446
|
if (wantsJson(flags) || wantsVerbose(flags)) {
|
|
434
447
|
const machinesById = new Map();
|
package/apps/cli/package.json
CHANGED
package/apps/daemon/package.json
CHANGED
package/package.json
CHANGED
|
@@ -563,9 +563,11 @@ export class ControllerClient {
|
|
|
563
563
|
importRequestId: requestId,
|
|
564
564
|
name,
|
|
565
565
|
...(input.providerName ? { providerName: input.providerName } : {}),
|
|
566
|
+
...(input.hiddenFromWorkspace ? { hiddenFromWorkspace: true, visibility: "hidden" } : {}),
|
|
566
567
|
importedFromRuntimeSessionId: runtimeSessionId,
|
|
567
568
|
importedRuntimeState: input.runtimeState,
|
|
568
569
|
importedSummary: input.summary,
|
|
570
|
+
...(input.source ? { source: input.source } : {}),
|
|
569
571
|
}),
|
|
570
572
|
});
|
|
571
573
|
if (result.sessionId) {
|
|
@@ -366,8 +366,10 @@ export type ImportHistoricalSessionInput = {
|
|
|
366
366
|
cwd: string;
|
|
367
367
|
runtimeSessionId: string;
|
|
368
368
|
name: string;
|
|
369
|
+
hiddenFromWorkspace?: boolean;
|
|
369
370
|
providerName?: string;
|
|
370
371
|
runtimeState?: HistoricalSessionSnapshot["runtimeState"];
|
|
372
|
+
source?: string;
|
|
371
373
|
summary?: string;
|
|
372
374
|
};
|
|
373
375
|
export type ForkInput = {
|