31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
# File 'lib/codex_notify/cli.rb', line 31
def main(argv = nil, stdin: nil, stderr: $stderr, stdout: $stdout, legacy_checkout_root: nil)
args = CodexNotify::Config.parse_args(argv, stderr:, legacy_checkout_root:)
if args.migrate_config
env_path = args.env_file if args.env_file_explicit
return ConfigMigrator.new(legacy_checkout_root:, stdout:, stderr:).run(
env_path:,
config_path: args.config_file
)
end
if args.outbox_action
return OutboxCommands.run(
action: args.outbox_action,
id: args.outbox_id,
outbox_dir: args.outbox_dir,
token: args.token,
channel: args.channel,
state_file: Pathname(args.outbox_dir).join('thread-state.json'),
stdout:,
stderr:
)
end
unless args.token && args.channel
stderr.puts('ERROR: need --token/--channel or a Slack destination from environment or config file')
return 2
end
cwd = Dir.pwd
title = args.title || "Codex run: #{File.basename(cwd)}"
session_file = args.session_file ? Pathname(args.session_file) : CodexNotify::SessionLog.find_latest_session_file(Pathname(args.sessions_dir))
unless session_file&.exist?
stderr.puts('ERROR: no Codex session log file found')
return 2
end
root_message = CodexNotify::MessageFormatter.build_root_message(
title,
cwd,
user_name: args.user_name,
session_id: CodexNotify::SessionLog.session_id_from_log(session_file) ||
CodexNotify::SessionLog.session_id_from_path(session_file)
)
outbox = SlackOutbox.new(args.outbox_dir)
store = HookStore.new(Pathname(args.outbox_dir).join('thread-state.json'))
publisher = DurableSlackPublisher.new(
client: PostClient.new(args.token, args.channel, method(:slack_post)),
store:,
outbox:,
channel: args.channel,
throttle_sec: args.throttle_sec
)
code = CodexNotify::StreamProcessor.process_codex_log_stream(
CodexNotify::SessionLog.iter_follow_lines(
session_file,
poll_sec: args.poll_sec,
once: args.once,
start_at_end: !args.once,
sleep_func: method(:sleep)
),
token: args.token,
channel: args.channel,
root_message:,
initial_prompt: args.prompt,
user_name: args.user_name,
include_tools: args.include_tools,
throttle_sec: args.throttle_sec,
post_func: method(:slack_post),
publisher:
)
stderr.puts('ERROR: Slack delivery requires outbox review; run --outbox-status') if code == 1
code
rescue Interrupt
stderr.puts('Stopped.')
0
rescue SlackOutbox::Error => e
stderr.puts("ERROR: #{e.message}")
1
rescue Config::Error, ConfigMigrator::Error, OptionParser::ParseError => e
stderr.puts("ERROR: #{e.message}")
2
end
|