Skip to content

Commit 77a92bb

Browse files
committed
feat: create automations in the current IM chat
1 parent 034040c commit 77a92bb

8 files changed

Lines changed: 169 additions & 28 deletions

File tree

‎go.mod‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/flashcatcloud/flashduty-cli
33
go 1.26.0
44

55
require (
6-
github.com/flashcatcloud/go-flashduty v0.15.16
6+
github.com/flashcatcloud/go-flashduty v0.15.17-0.20261014102327-2f2d5497df53
77
github.com/mattn/go-runewidth v0.0.30
88
github.com/spf13/cobra v1.10.2
99
github.com/spf13/pflag v1.0.10

‎go.sum‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY=
22
github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
33
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
4-
github.com/flashcatcloud/go-flashduty v0.15.16 h1:h04Z0NGxB4ujlfKliVPD8QhgLgBObeAoRMz9rXJpnes=
5-
github.com/flashcatcloud/go-flashduty v0.15.16/go.mod h1:YpHiTYXR5NXBI/rGRZfUy537XMkhdCkwA8NW1QoRHwk=
4+
github.com/flashcatcloud/go-flashduty v0.15.17-0.20261014102327-2f2d5497df53 h1:v6tVZmttBD67/gcdQym1SmY1TavYXd95WU3AQAog9VQ=
5+
github.com/flashcatcloud/go-flashduty v0.15.17-0.20261014102327-2f2d5497df53/go.mod h1:YpHiTYXR5NXBI/rGRZfUy537XMkhdCkwA8NW1QoRHwk=
66
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
77
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
88
github.com/mattn/go-runewidth v0.0.30 h1:+KUuiDA4fF0R1p5FeueHefjDm+GIM+kWfFnDjybOPgk=

‎internal/cli/automation.go‎

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func newAutomationCreateCmd() *cobra.Command {
4949
promptFile string
5050
environmentKind string
5151
environmentID string
52+
runIn string
5253
timezone string
5354
)
5455

@@ -58,8 +59,14 @@ func newAutomationCreateCmd() *cobra.Command {
5859
Long: curatedLong(`Create an AI SRE Automation.
5960
6061
By default the rule is enabled. Use --disabled only when the user explicitly
61-
asks to create it disabled. team_id=0 means personal scope; --team-id >0 creates
62-
the rule under that team. The scope is immutable after creation.
62+
asks to create it disabled. Omit --team-id to let the server inherit the live
63+
IM conversation scope when available; --team-id 0 explicitly selects personal
64+
scope and --team-id >0 selects that team. The scope is immutable after creation.
65+
66+
When this command is invoked from a live IM conversation, omitting --run-in
67+
lets the server bind the Automation to that current chat. Use --run-in web to
68+
run it in the web console instead. Do not supply chat, platform, integration,
69+
or session identifiers: the server resolves and authorizes the current chat.
6370
6471
Schedule helpers build a 5-field cron expression evaluated in the rule's
6572
timezone. Pass --timezone when the user names one. When --timezone is
@@ -86,6 +93,12 @@ the rule under that team. The scope is immutable after creation.
8693
if strings.TrimSpace(taskPrompt) == "" {
8794
return fmt.Errorf("one of --prompt or --prompt-file is required")
8895
}
96+
if cmd.Flags().Changed("run-in") {
97+
runIn = strings.TrimSpace(runIn)
98+
if runIn != "current-chat" && runIn != "web" {
99+
return fmt.Errorf("--run-in must be current-chat or web")
100+
}
101+
}
89102

90103
effectiveScheduleEnabled := scheduleEnabled
91104
cron, err := resolveAutomationCreateCron(cmd, schedule, at, weekday, cronExpr, httpPostTrigger, &effectiveScheduleEnabled)
@@ -95,7 +108,6 @@ the rule under that team. The scope is immutable after creation.
95108

96109
req := &flashduty.AutomationRuleCreateRequest{
97110
Name: name,
98-
TeamID: teamID,
99111
CronExpr: cron,
100112
Enabled: !disabled,
101113
ScheduleTriggerEnabled: flashduty.Bool(effectiveScheduleEnabled),
@@ -104,6 +116,12 @@ the rule under that team. The scope is immutable after creation.
104116
EnvironmentKind: strings.TrimSpace(environmentKind),
105117
EnvironmentID: strings.TrimSpace(environmentID),
106118
}
119+
if cmd.Flags().Changed("team-id") {
120+
req.TeamID = flashduty.Int64(teamID)
121+
}
122+
if cmd.Flags().Changed("run-in") {
123+
req.RunIn = flashduty.String(runIn)
124+
}
107125
if cmd.Flags().Changed("timezone") {
108126
req.Timezone = strings.TrimSpace(timezone)
109127
}
@@ -117,7 +135,8 @@ the rule under that team. The scope is immutable after creation.
117135
}
118136

119137
cmd.Flags().StringVar(&name, "name", "", "Automation name")
120-
cmd.Flags().Int64Var(&teamID, "team-id", 0, "Scope team ID; 0 means personal scope")
138+
cmd.Flags().Int64Var(&teamID, "team-id", 0, "Scope team ID; omit to inherit IM scope when available, or pass 0 for personal scope")
139+
cmd.Flags().StringVar(&runIn, "run-in", "", "Execution destination: current-chat or web; omit for the server default")
121140
cmd.Flags().StringVar(&schedule, "schedule", "", "Schedule helper: hourly, daily, weekly, or cron")
122141
cmd.Flags().StringVar(&at, "at", "", "Local time in HH:MM (rule's timezone); for hourly schedules, only the minute is used. "+automationTimezoneNote)
123142
cmd.Flags().StringVar(&weekday, "weekday", "", "Weekday for weekly schedules: sun, mon, tue, wed, thu, fri, sat, or 0-7")
@@ -132,6 +151,7 @@ the rule under that team. The scope is immutable after creation.
132151
cmd.Flags().StringVar(&environmentID, "environment-id", "", "BYOC Runner ID when --environment-kind=byoc")
133152
registerEnumFlag(cmd, "schedule", "hourly", "daily", "weekly", "cron")
134153
registerEnumFlag(cmd, "environment-kind", "cloud", "byoc")
154+
registerEnumFlag(cmd, "run-in", "current-chat", "web")
135155
return cmd
136156
}
137157

‎internal/cli/automation_test.go‎

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,65 @@ func TestAutomationCreateDailyDefaultsEnabled(t *testing.T) {
3434
assertBody(t, stub.lastBody, "prompt", "Summarize yesterday's incidents")
3535
}
3636

37+
func TestAutomationCreateExecutionDestinationWireContract(t *testing.T) {
38+
t.Run("omits server selected destination", func(t *testing.T) {
39+
saveAndResetGlobals(t)
40+
stub := newGFStub(t)
41+
42+
_, err := execCommand(
43+
"automation", "create",
44+
"--name", "Daily SRE brief",
45+
"--prompt", "Summarize yesterday's incidents",
46+
"--json",
47+
)
48+
if err != nil {
49+
t.Fatalf("[automation-create-omitted-destination] unexpected error: %v", err)
50+
}
51+
for _, field := range []string{"run_in", "team_id"} {
52+
if _, ok := stub.lastBody[field]; ok {
53+
t.Fatalf("[automation-create-omitted-destination] %s must be omitted, body=%#v", field, stub.lastBody)
54+
}
55+
}
56+
})
57+
58+
t.Run("sends explicit chat and personal scope", func(t *testing.T) {
59+
saveAndResetGlobals(t)
60+
stub := newGFStub(t)
61+
62+
_, err := execCommand(
63+
"automation", "create",
64+
"--name", "Daily SRE brief",
65+
"--prompt", "Summarize yesterday's incidents",
66+
"--run-in", "current-chat",
67+
"--team-id", "0",
68+
"--json",
69+
)
70+
if err != nil {
71+
t.Fatalf("[automation-create-current-chat] unexpected error: %v", err)
72+
}
73+
assertBody(t, stub.lastBody, "run_in", "current-chat")
74+
assertBody(t, stub.lastBody, "team_id", float64(0))
75+
})
76+
77+
t.Run("rejects invalid destination before request", func(t *testing.T) {
78+
saveAndResetGlobals(t)
79+
stub := newGFStub(t)
80+
81+
_, err := execCommand(
82+
"automation", "create",
83+
"--name", "Daily SRE brief",
84+
"--prompt", "Summarize yesterday's incidents",
85+
"--run-in", "other-chat",
86+
)
87+
if err == nil || !strings.Contains(err.Error(), "--run-in must be current-chat or web") {
88+
t.Fatalf("[automation-create-invalid-destination] err = %v", err)
89+
}
90+
if stub.requests != 0 {
91+
t.Fatalf("[automation-create-invalid-destination] request must not reach API, requests=%d", stub.requests)
92+
}
93+
})
94+
}
95+
3796
func TestAutomationScheduleHelpDocumentsTimezone(t *testing.T) {
3897
saveAndResetGlobals(t)
3998

@@ -279,6 +338,18 @@ func TestAutomationUpdateDoesNotExposeScopeFlag(t *testing.T) {
279338
}
280339
}
281340

341+
func TestSafariAutomationRuleUpdateDoesNotExposeExecutionTargetFlags(t *testing.T) {
342+
saveAndResetGlobals(t)
343+
newGFStub(t)
344+
345+
for _, flag := range []string{"--team-id", "--run-in"} {
346+
_, err := execCommand("safari", "automation-rule-update", "auto_123", flag, "0")
347+
if err == nil || !strings.Contains(err.Error(), "unknown flag") {
348+
t.Fatalf("[safari-automation-update-target] %s err = %v", flag, err)
349+
}
350+
}
351+
}
352+
282353
func TestAutomationFireSendsBearerToken(t *testing.T) {
283354
saveAndResetGlobals(t)
284355
stub := newGFStub(t)

0 commit comments

Comments
 (0)