@dalmasonto/taskflow-mcp 1.0.35 → 2.0.0
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/README.md +138 -193
- package/dist/attachment-download.d.ts +74 -0
- package/dist/attachment-download.js +193 -0
- package/dist/attachment-download.js.map +1 -0
- package/dist/attachments.d.ts +23 -0
- package/dist/attachments.js +66 -0
- package/dist/attachments.js.map +1 -0
- package/dist/client.d.ts +206 -0
- package/dist/client.js +279 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +137 -18
- package/dist/config.js +187 -106
- package/dist/config.js.map +1 -0
- package/dist/connect.d.ts +89 -0
- package/dist/connect.js +269 -0
- package/dist/connect.js.map +1 -0
- package/dist/doctor.d.ts +24 -0
- package/dist/doctor.js +120 -0
- package/dist/doctor.js.map +1 -0
- package/dist/events.d.ts +186 -0
- package/dist/events.js +407 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +121 -218
- package/dist/index.js.map +1 -0
- package/dist/instructions.d.ts +12 -0
- package/dist/instructions.js +114 -0
- package/dist/instructions.js.map +1 -0
- package/dist/mint.d.ts +62 -0
- package/dist/mint.js +135 -0
- package/dist/mint.js.map +1 -0
- package/dist/mirror.d.ts +68 -0
- package/dist/mirror.js +103 -0
- package/dist/mirror.js.map +1 -0
- package/dist/pane-queue.d.ts +29 -0
- package/dist/pane-queue.js +35 -0
- package/dist/pane-queue.js.map +1 -0
- package/dist/prompts.d.ts +79 -0
- package/dist/prompts.js +211 -0
- package/dist/prompts.js.map +1 -0
- package/dist/resolve.d.ts +72 -0
- package/dist/resolve.js +89 -0
- package/dist/resolve.js.map +1 -0
- package/dist/runtime.d.ts +54 -0
- package/dist/runtime.js +321 -0
- package/dist/runtime.js.map +1 -0
- package/dist/server.d.ts +56 -0
- package/dist/server.js +793 -0
- package/dist/server.js.map +1 -0
- package/dist/session-identifier.d.ts +48 -0
- package/dist/session-identifier.js +44 -0
- package/dist/session-identifier.js.map +1 -0
- package/dist/sessions-store.d.ts +38 -0
- package/dist/sessions-store.js +88 -0
- package/dist/sessions-store.js.map +1 -0
- package/dist/tmux.d.ts +200 -0
- package/dist/tmux.js +580 -0
- package/dist/tmux.js.map +1 -0
- package/hooks/metadata.mjs +99 -0
- package/hooks/permission-prompt.mjs +100 -0
- package/hooks/taskflow-hook.mjs +499 -0
- package/hooks/tool-logging.mjs +63 -0
- package/package.json +38 -29
- package/dist/agent-registry.d.ts +0 -28
- package/dist/agent-registry.js +0 -158
- package/dist/db.d.ts +0 -5
- package/dist/db.js +0 -220
- package/dist/helpers.d.ts +0 -21
- package/dist/helpers.js +0 -27
- package/dist/resources.d.ts +0 -2
- package/dist/resources.js +0 -89
- package/dist/retry.d.ts +0 -34
- package/dist/retry.js +0 -94
- package/dist/sse.d.ts +0 -10
- package/dist/sse.js +0 -824
- package/dist/tmux-bridge.d.ts +0 -13
- package/dist/tmux-bridge.js +0 -217
- package/dist/tools/activity.d.ts +0 -39
- package/dist/tools/activity.js +0 -152
- package/dist/tools/agent-inbox.d.ts +0 -12
- package/dist/tools/agent-inbox.js +0 -272
- package/dist/tools/agent.d.ts +0 -14
- package/dist/tools/agent.js +0 -168
- package/dist/tools/analytics.d.ts +0 -21
- package/dist/tools/analytics.js +0 -191
- package/dist/tools/checkpoint.d.ts +0 -27
- package/dist/tools/checkpoint.js +0 -105
- package/dist/tools/notifications.d.ts +0 -31
- package/dist/tools/notifications.js +0 -59
- package/dist/tools/projects.d.ts +0 -55
- package/dist/tools/projects.js +0 -112
- package/dist/tools/settings.d.ts +0 -19
- package/dist/tools/settings.js +0 -73
- package/dist/tools/tasks.d.ts +0 -105
- package/dist/tools/tasks.js +0 -403
- package/dist/tools/terminal.d.ts +0 -4
- package/dist/tools/terminal.js +0 -98
- package/dist/tools/timer.d.ts +0 -37
- package/dist/tools/timer.js +0 -154
- package/dist/types.d.ts +0 -83
- package/dist/types.js +0 -30
package/dist/config.d.ts
CHANGED
|
@@ -1,21 +1,140 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Load and validate `.taskflow.json`, then resolve one profile into a flat,
|
|
3
|
+
* ready-to-use identity for the HTTP client.
|
|
4
|
+
*
|
|
5
|
+
* The file is the stable per-repo credential a human creates via the dashboard's
|
|
6
|
+
* **API Base** page:
|
|
7
|
+
*
|
|
8
|
+
* ```json
|
|
9
|
+
* { "server": "http://localhost:8000", "project": 1, "default_profile": "main",
|
|
10
|
+
* "profiles": {
|
|
11
|
+
* "main": { "agent_id": 12, "key": "tfk_…", "display_name": "Builder" },
|
|
12
|
+
* "reviewer": { "agent_id": 13, "key": "tfk_…", "display_name": "Reviewer" }
|
|
13
|
+
* } }
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* File resolution: an explicit path (or `TASKFLOW_CONFIG`) wins; otherwise we
|
|
17
|
+
* walk up from a start directory to the first `.taskflow.json`.
|
|
18
|
+
*
|
|
19
|
+
* Profile resolution (highest priority first):
|
|
20
|
+
* 1. an explicit `profile` argument (a per-tool-call override),
|
|
21
|
+
* 2. the `TASKFLOW_PROFILE` env var,
|
|
22
|
+
* 3. the file's `default_profile`,
|
|
23
|
+
* 4. the literal `"main"`.
|
|
24
|
+
*
|
|
25
|
+
* Every function that reads the environment or filesystem takes those as
|
|
26
|
+
* explicit, defaultable options so the whole module is unit-testable without
|
|
27
|
+
* touching real `process.env` or `process.cwd()`.
|
|
28
|
+
*/
|
|
29
|
+
import { z } from "zod";
|
|
30
|
+
export declare const CONFIG_FILENAME = ".taskflow.json";
|
|
31
|
+
export declare const DEFAULT_PROFILE_NAME = "main";
|
|
32
|
+
/** One profile block as it appears in the file. */
|
|
33
|
+
export declare const profileSchema: z.ZodObject<{
|
|
34
|
+
agent_id: z.ZodNumber;
|
|
35
|
+
key: z.ZodString;
|
|
36
|
+
display_name: z.ZodOptional<z.ZodString>;
|
|
37
|
+
}, z.core.$strip>;
|
|
38
|
+
/** The whole `.taskflow.json` file. */
|
|
39
|
+
export declare const taskflowConfigSchema: z.ZodObject<{
|
|
40
|
+
server: z.ZodString;
|
|
41
|
+
project: z.ZodNumber;
|
|
42
|
+
default_profile: z.ZodOptional<z.ZodString>;
|
|
43
|
+
profiles: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
44
|
+
agent_id: z.ZodNumber;
|
|
45
|
+
key: z.ZodString;
|
|
46
|
+
display_name: z.ZodOptional<z.ZodString>;
|
|
47
|
+
}, z.core.$strip>>;
|
|
48
|
+
}, z.core.$strip>;
|
|
49
|
+
export type RawProfile = z.infer<typeof profileSchema>;
|
|
50
|
+
export type TaskflowConfig = z.infer<typeof taskflowConfigSchema>;
|
|
51
|
+
/** A profile flattened with its file-level context — everything the client needs. */
|
|
52
|
+
export interface ResolvedProfile {
|
|
53
|
+
server: string;
|
|
54
|
+
project: number;
|
|
55
|
+
profileName: string;
|
|
56
|
+
agentId: number;
|
|
57
|
+
key: string;
|
|
58
|
+
displayName: string;
|
|
59
|
+
/** Absolute path the config was read from (empty when parsed from a string). */
|
|
60
|
+
configPath: string;
|
|
61
|
+
}
|
|
62
|
+
/** A clear, user-facing configuration error (never a raw stack). */
|
|
63
|
+
export declare class ConfigError extends Error {
|
|
64
|
+
constructor(message: string);
|
|
65
|
+
}
|
|
66
|
+
export interface FindConfigOptions {
|
|
67
|
+
/** An explicit path to the file; skips the walk-up entirely. */
|
|
68
|
+
configPath?: string | undefined;
|
|
69
|
+
/** Where to start the upward walk (defaults to `process.cwd()`). */
|
|
70
|
+
startDir?: string | undefined;
|
|
71
|
+
/** Environment source (defaults to `process.env`). */
|
|
72
|
+
env?: NodeJS.ProcessEnv;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Resolve the absolute path of the `.taskflow.json` to use. Precedence:
|
|
76
|
+
* explicit `configPath` > `TASKFLOW_CONFIG` env > walk up from `startDir`.
|
|
77
|
+
* Throws {@link ConfigError} when nothing is found.
|
|
78
|
+
*/
|
|
79
|
+
export declare function findConfigPath(options?: FindConfigOptions): string;
|
|
80
|
+
/** Validate an already-parsed object (or JSON text) into a {@link TaskflowConfig}. */
|
|
81
|
+
export declare function parseConfig(input: string | unknown, sourceLabel?: string): TaskflowConfig;
|
|
82
|
+
/** Read + validate the config file at an absolute path. */
|
|
83
|
+
export declare function loadConfigFile(configPath: string): TaskflowConfig;
|
|
84
|
+
export interface ResolveProfileOptions {
|
|
85
|
+
/** An explicit per-call profile override (highest precedence). */
|
|
86
|
+
profile?: string | undefined;
|
|
87
|
+
/** Environment source (defaults to `process.env`); reads `TASKFLOW_PROFILE`. */
|
|
88
|
+
env?: NodeJS.ProcessEnv;
|
|
89
|
+
/** Path the config came from, for error messages and the resolved result. */
|
|
90
|
+
configPath?: string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Pick the effective profile name using the documented precedence:
|
|
94
|
+
* argument > `TASKFLOW_PROFILE` > `default_profile` > `"main"`.
|
|
95
|
+
*/
|
|
96
|
+
export declare function chooseProfileName(config: TaskflowConfig, options?: ResolveProfileOptions): string;
|
|
97
|
+
/**
|
|
98
|
+
* Resolve `config` + options into a flat {@link ResolvedProfile}. Throws
|
|
99
|
+
* {@link ConfigError} if the chosen profile is not present in the file.
|
|
100
|
+
*/
|
|
101
|
+
export declare function resolveProfile(config: TaskflowConfig, options?: ResolveProfileOptions): ResolvedProfile;
|
|
102
|
+
/** One selectable identity, as offered to the human choosing among them. */
|
|
103
|
+
export interface ProfileChoice {
|
|
104
|
+
name: string;
|
|
105
|
+
display_name: string;
|
|
106
|
+
/** True for the file's `default_profile` — a hint, never a decision. */
|
|
107
|
+
recommended: boolean;
|
|
108
|
+
/** Filled in by the caller from live session data; omitted if unknown. */
|
|
109
|
+
in_use?: boolean;
|
|
110
|
+
}
|
|
111
|
+
export type ProfileResolution = {
|
|
112
|
+
kind: "resolved";
|
|
113
|
+
profile: ResolvedProfile;
|
|
114
|
+
} | {
|
|
115
|
+
kind: "ambiguous";
|
|
116
|
+
profiles: ProfileChoice[];
|
|
117
|
+
};
|
|
118
|
+
export interface AskProfileOptions extends ResolveProfileOptions {
|
|
119
|
+
/** This terminal's remembered pick, if any (see `sessions-store.ts`). */
|
|
120
|
+
sticky?: string | undefined;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Resolve a profile, or report that a human has to choose.
|
|
124
|
+
*
|
|
125
|
+
* Ambiguous means: several profiles defined, and nothing said which one this
|
|
126
|
+
* terminal is. `default_profile` deliberately does NOT settle it — `--mint`
|
|
127
|
+
* adds profiles without moving it, so honouring it would mean the prompt never
|
|
128
|
+
* fires and two terminals silently share one identity, which is the bug.
|
|
129
|
+
*
|
|
130
|
+
* `resolveProfile` keeps its throwing contract for callers that already have a
|
|
131
|
+
* name (`--tmux --profile=x`, `--mint`, a per-tool `profile` argument).
|
|
132
|
+
*/
|
|
133
|
+
export declare function resolveProfileOrAsk(config: TaskflowConfig, options?: AskProfileOptions): ProfileResolution;
|
|
134
|
+
export interface LoadProfileOptions extends FindConfigOptions, ResolveProfileOptions {
|
|
10
135
|
}
|
|
11
|
-
/** Keys that live in the config file (not SQLite) */
|
|
12
|
-
export declare const SERVER_CONFIG_KEYS: Set<string>;
|
|
13
|
-
export declare function getConfig(): TaskFlowConfig;
|
|
14
|
-
/** Check if a setting key is a server-level config key */
|
|
15
|
-
export declare function isServerConfigKey(key: string): boolean;
|
|
16
136
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* Returns the updated config value.
|
|
137
|
+
* The one-call convenience the bin entry and hooks use: find the file, load +
|
|
138
|
+
* validate it, and resolve the chosen profile into a {@link ResolvedProfile}.
|
|
20
139
|
*/
|
|
21
|
-
export declare function
|
|
140
|
+
export declare function loadProfile(options?: LoadProfileOptions): ResolvedProfile;
|
package/dist/config.js
CHANGED
|
@@ -1,125 +1,206 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
import { existsSync } from
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Load and validate `.taskflow.json`, then resolve one profile into a flat,
|
|
3
|
+
* ready-to-use identity for the HTTP client.
|
|
4
|
+
*
|
|
5
|
+
* The file is the stable per-repo credential a human creates via the dashboard's
|
|
6
|
+
* **API Base** page:
|
|
7
|
+
*
|
|
8
|
+
* ```json
|
|
9
|
+
* { "server": "http://localhost:8000", "project": 1, "default_profile": "main",
|
|
10
|
+
* "profiles": {
|
|
11
|
+
* "main": { "agent_id": 12, "key": "tfk_…", "display_name": "Builder" },
|
|
12
|
+
* "reviewer": { "agent_id": 13, "key": "tfk_…", "display_name": "Reviewer" }
|
|
13
|
+
* } }
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* File resolution: an explicit path (or `TASKFLOW_CONFIG`) wins; otherwise we
|
|
17
|
+
* walk up from a start directory to the first `.taskflow.json`.
|
|
18
|
+
*
|
|
19
|
+
* Profile resolution (highest priority first):
|
|
20
|
+
* 1. an explicit `profile` argument (a per-tool-call override),
|
|
21
|
+
* 2. the `TASKFLOW_PROFILE` env var,
|
|
22
|
+
* 3. the file's `default_profile`,
|
|
23
|
+
* 4. the literal `"main"`.
|
|
24
|
+
*
|
|
25
|
+
* Every function that reads the environment or filesystem takes those as
|
|
26
|
+
* explicit, defaultable options so the whole module is unit-testable without
|
|
27
|
+
* touching real `process.env` or `process.cwd()`.
|
|
28
|
+
*/
|
|
29
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
30
|
+
import { dirname, join, resolve } from "node:path";
|
|
31
|
+
import { z } from "zod";
|
|
32
|
+
export const CONFIG_FILENAME = ".taskflow.json";
|
|
33
|
+
export const DEFAULT_PROFILE_NAME = "main";
|
|
34
|
+
/** One profile block as it appears in the file. */
|
|
35
|
+
export const profileSchema = z.object({
|
|
36
|
+
agent_id: z.number().int(),
|
|
37
|
+
key: z.string().min(1, "profile key must not be empty"),
|
|
38
|
+
display_name: z.string().optional(),
|
|
39
|
+
});
|
|
40
|
+
/** The whole `.taskflow.json` file. */
|
|
41
|
+
export const taskflowConfigSchema = z.object({
|
|
42
|
+
server: z.string().min(1, "server must not be empty"),
|
|
43
|
+
project: z.number().int(),
|
|
44
|
+
default_profile: z.string().optional(),
|
|
45
|
+
profiles: z
|
|
46
|
+
.record(z.string(), profileSchema)
|
|
47
|
+
.refine((p) => Object.keys(p).length > 0, {
|
|
48
|
+
message: "profiles must contain at least one profile",
|
|
49
|
+
}),
|
|
50
|
+
});
|
|
51
|
+
/** A clear, user-facing configuration error (never a raw stack). */
|
|
52
|
+
export class ConfigError extends Error {
|
|
53
|
+
constructor(message) {
|
|
54
|
+
super(message);
|
|
55
|
+
this.name = "ConfigError";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Resolve the absolute path of the `.taskflow.json` to use. Precedence:
|
|
60
|
+
* explicit `configPath` > `TASKFLOW_CONFIG` env > walk up from `startDir`.
|
|
61
|
+
* Throws {@link ConfigError} when nothing is found.
|
|
62
|
+
*/
|
|
63
|
+
export function findConfigPath(options = {}) {
|
|
64
|
+
const env = options.env ?? process.env;
|
|
65
|
+
const explicit = options.configPath ?? env.TASKFLOW_CONFIG;
|
|
66
|
+
if (explicit) {
|
|
67
|
+
const abs = resolve(explicit);
|
|
68
|
+
if (!existsSync(abs)) {
|
|
69
|
+
throw new ConfigError(`TaskFlow config not found at ${abs}`);
|
|
43
70
|
}
|
|
44
|
-
return
|
|
71
|
+
return abs;
|
|
45
72
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
73
|
+
let dir = resolve(options.startDir ?? process.cwd());
|
|
74
|
+
// Walk up to the filesystem root looking for the file.
|
|
75
|
+
// eslint-disable-next-line no-constant-condition
|
|
76
|
+
while (true) {
|
|
77
|
+
const candidate = join(dir, CONFIG_FILENAME);
|
|
78
|
+
if (existsSync(candidate)) {
|
|
79
|
+
return candidate;
|
|
80
|
+
}
|
|
81
|
+
const parent = dirname(dir);
|
|
82
|
+
if (parent === dir) {
|
|
83
|
+
break;
|
|
50
84
|
}
|
|
51
|
-
|
|
52
|
-
return {};
|
|
85
|
+
dir = parent;
|
|
53
86
|
}
|
|
87
|
+
throw new ConfigError(`Could not find ${CONFIG_FILENAME} in ${resolve(options.startDir ?? process.cwd())} or any parent directory. ` +
|
|
88
|
+
`Create one (link an agent in the dashboard's API Base page) or set TASKFLOW_CONFIG.`);
|
|
54
89
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (process.env.TASKFLOW_HOST) {
|
|
66
|
-
config.host = process.env.TASKFLOW_HOST;
|
|
67
|
-
}
|
|
68
|
-
if (process.env.TASKFLOW_LOG_LEVEL) {
|
|
69
|
-
config.logLevel = process.env.TASKFLOW_LOG_LEVEL;
|
|
90
|
+
/** Validate an already-parsed object (or JSON text) into a {@link TaskflowConfig}. */
|
|
91
|
+
export function parseConfig(input, sourceLabel = "<string>") {
|
|
92
|
+
let data;
|
|
93
|
+
if (typeof input === "string") {
|
|
94
|
+
try {
|
|
95
|
+
data = JSON.parse(input);
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
throw new ConfigError(`Failed to parse ${sourceLabel} as JSON: ${err.message}`);
|
|
99
|
+
}
|
|
70
100
|
}
|
|
71
|
-
|
|
72
|
-
|
|
101
|
+
else {
|
|
102
|
+
data = input;
|
|
73
103
|
}
|
|
74
|
-
|
|
75
|
-
|
|
104
|
+
const result = taskflowConfigSchema.safeParse(data);
|
|
105
|
+
if (!result.success) {
|
|
106
|
+
const issues = result.error.issues
|
|
107
|
+
.map((i) => ` - ${i.path.join(".") || "(root)"}: ${i.message}`)
|
|
108
|
+
.join("\n");
|
|
109
|
+
throw new ConfigError(`Invalid ${sourceLabel}:\n${issues}`);
|
|
76
110
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
111
|
+
return result.data;
|
|
112
|
+
}
|
|
113
|
+
/** Read + validate the config file at an absolute path. */
|
|
114
|
+
export function loadConfigFile(configPath) {
|
|
115
|
+
let text;
|
|
116
|
+
try {
|
|
117
|
+
text = readFileSync(configPath, "utf8");
|
|
83
118
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
config.host = process.argv[hostArgIdx + 1];
|
|
119
|
+
catch (err) {
|
|
120
|
+
throw new ConfigError(`Failed to read ${configPath}: ${err.message}`);
|
|
87
121
|
}
|
|
88
|
-
return
|
|
122
|
+
return parseConfig(text, configPath);
|
|
89
123
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
124
|
+
/**
|
|
125
|
+
* Pick the effective profile name using the documented precedence:
|
|
126
|
+
* argument > `TASKFLOW_PROFILE` > `default_profile` > `"main"`.
|
|
127
|
+
*/
|
|
128
|
+
export function chooseProfileName(config, options = {}) {
|
|
129
|
+
const env = options.env ?? process.env;
|
|
130
|
+
const fromArg = options.profile?.trim();
|
|
131
|
+
const fromEnv = env.TASKFLOW_PROFILE?.trim();
|
|
132
|
+
return ((fromArg && fromArg.length > 0 ? fromArg : undefined) ??
|
|
133
|
+
(fromEnv && fromEnv.length > 0 ? fromEnv : undefined) ??
|
|
134
|
+
config.default_profile ??
|
|
135
|
+
DEFAULT_PROFILE_NAME);
|
|
98
136
|
}
|
|
99
|
-
/**
|
|
100
|
-
|
|
101
|
-
|
|
137
|
+
/**
|
|
138
|
+
* Resolve `config` + options into a flat {@link ResolvedProfile}. Throws
|
|
139
|
+
* {@link ConfigError} if the chosen profile is not present in the file.
|
|
140
|
+
*/
|
|
141
|
+
export function resolveProfile(config, options = {}) {
|
|
142
|
+
const profileName = chooseProfileName(config, options);
|
|
143
|
+
const profile = config.profiles[profileName];
|
|
144
|
+
if (!profile) {
|
|
145
|
+
const available = Object.keys(config.profiles).join(", ") || "(none)";
|
|
146
|
+
throw new ConfigError(`Profile "${profileName}" is not defined in ${options.configPath ?? CONFIG_FILENAME}. Available profiles: ${available}.`);
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
server: config.server.replace(/\/+$/, ""),
|
|
150
|
+
project: config.project,
|
|
151
|
+
profileName,
|
|
152
|
+
agentId: profile.agent_id,
|
|
153
|
+
key: profile.key,
|
|
154
|
+
displayName: profile.display_name ?? profileName,
|
|
155
|
+
configPath: options.configPath ?? "",
|
|
156
|
+
};
|
|
102
157
|
}
|
|
103
|
-
// ─── write-back ──────────────────────────────────────────────────────
|
|
104
158
|
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
159
|
+
* Resolve a profile, or report that a human has to choose.
|
|
160
|
+
*
|
|
161
|
+
* Ambiguous means: several profiles defined, and nothing said which one this
|
|
162
|
+
* terminal is. `default_profile` deliberately does NOT settle it — `--mint`
|
|
163
|
+
* adds profiles without moving it, so honouring it would mean the prompt never
|
|
164
|
+
* fires and two terminals silently share one identity, which is the bug.
|
|
165
|
+
*
|
|
166
|
+
* `resolveProfile` keeps its throwing contract for callers that already have a
|
|
167
|
+
* name (`--tmux --profile=x`, `--mint`, a per-tool `profile` argument).
|
|
108
168
|
*/
|
|
109
|
-
export function
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
169
|
+
export function resolveProfileOrAsk(config, options = {}) {
|
|
170
|
+
const env = options.env ?? process.env;
|
|
171
|
+
const explicit = options.profile?.trim() || env.TASKFLOW_PROFILE?.trim();
|
|
172
|
+
if (explicit) {
|
|
173
|
+
// Unknown name is a real error, not an invitation to ask: the caller
|
|
174
|
+
// asserted an identity and got it wrong.
|
|
175
|
+
return { kind: "resolved", profile: resolveProfile(config, { ...options, profile: explicit }) };
|
|
114
176
|
}
|
|
115
|
-
|
|
116
|
-
|
|
177
|
+
const names = Object.keys(config.profiles);
|
|
178
|
+
// A stale sticky pick (profile since removed from the file) falls through to
|
|
179
|
+
// asking rather than throwing — the human never typed that name today.
|
|
180
|
+
const sticky = options.sticky?.trim();
|
|
181
|
+
if (sticky && config.profiles[sticky]) {
|
|
182
|
+
return { kind: "resolved", profile: resolveProfile(config, { ...options, profile: sticky }) };
|
|
117
183
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
writeFileSync(CONFIG_PATH, JSON.stringify(existing, null, 2) + '\n', 'utf-8');
|
|
121
|
-
// Update the in-memory singleton so get_setting reflects the change immediately
|
|
122
|
-
if (_config && key in DEFAULTS) {
|
|
123
|
-
_config[key] = value;
|
|
184
|
+
if (names.length === 1) {
|
|
185
|
+
return { kind: "resolved", profile: resolveProfile(config, { ...options, profile: names[0] }) };
|
|
124
186
|
}
|
|
187
|
+
const recommended = config.default_profile ?? DEFAULT_PROFILE_NAME;
|
|
188
|
+
return {
|
|
189
|
+
kind: "ambiguous",
|
|
190
|
+
profiles: names.map((name) => ({
|
|
191
|
+
name,
|
|
192
|
+
display_name: config.profiles[name]?.display_name ?? name,
|
|
193
|
+
recommended: name === recommended,
|
|
194
|
+
})),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* The one-call convenience the bin entry and hooks use: find the file, load +
|
|
199
|
+
* validate it, and resolve the chosen profile into a {@link ResolvedProfile}.
|
|
200
|
+
*/
|
|
201
|
+
export function loadProfile(options = {}) {
|
|
202
|
+
const configPath = findConfigPath(options);
|
|
203
|
+
const config = loadConfigFile(configPath);
|
|
204
|
+
return resolveProfile(config, { ...options, configPath });
|
|
125
205
|
}
|
|
206
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agent's connection to TaskFlow: register a session, prove liveness, and
|
|
3
|
+
* keep doing both for as long as the process lives.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this is its own module
|
|
6
|
+
*
|
|
7
|
+
* This ran as step one of `startMirrorForThisAgent`'s `start(pane)` callback,
|
|
8
|
+
* behind `mirror.ts`'s "no tmux pane, nothing to mirror" early return. A
|
|
9
|
+
* capability (presence) was smuggled inside a feature (terminal mirroring), so
|
|
10
|
+
* the feature's precondition became the capability's: an agent outside tmux
|
|
11
|
+
* never registered, never heartbeat, and never appeared in the dashboard.
|
|
12
|
+
*
|
|
13
|
+
* Mirroring is now layered on top of this — `onSession` hands the caller a live
|
|
14
|
+
* session to attach the event stream and the pane mirror to.
|
|
15
|
+
*
|
|
16
|
+
* Retries are UNBOUNDED. The old startup gave up after 8 attempts (~2 minutes)
|
|
17
|
+
* and stayed dead for the life of the process, which is indistinguishable from
|
|
18
|
+
* the bug this module fixes: starting the MCP before the backend is a normal
|
|
19
|
+
* ordering, not an error.
|
|
20
|
+
*/
|
|
21
|
+
import { TaskflowClient } from "./client.js";
|
|
22
|
+
import type { ResolvedProfile } from "./config.js";
|
|
23
|
+
export type ConnectionState = "starting" | "active" | "retrying" | "needs_profile" | "stopped";
|
|
24
|
+
export interface ConnectionStatus {
|
|
25
|
+
state: ConnectionState;
|
|
26
|
+
/** Why, in the caller's words — always set for `retrying` and `needs_profile`. */
|
|
27
|
+
detail?: string;
|
|
28
|
+
attempts: number;
|
|
29
|
+
/** The live session row id, once registered. */
|
|
30
|
+
session?: number;
|
|
31
|
+
/**
|
|
32
|
+
* WHICH profile this connection is for. Absent before a connection exists
|
|
33
|
+
* (`starting` / `needs_profile` after a reset).
|
|
34
|
+
*
|
|
35
|
+
* A session id on its own is not enough for a caller to reuse: a tool called
|
|
36
|
+
* with an explicit `profile:` other than the connected one would send another
|
|
37
|
+
* agent's session under this profile's credential, which the backend rejects.
|
|
38
|
+
*/
|
|
39
|
+
profile?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface ConnectedContext {
|
|
42
|
+
client: TaskflowClient;
|
|
43
|
+
session: number;
|
|
44
|
+
profile: ResolvedProfile;
|
|
45
|
+
pane: string | null;
|
|
46
|
+
}
|
|
47
|
+
export interface ConnectOptions {
|
|
48
|
+
profile: ResolvedProfile;
|
|
49
|
+
/** The tmux pane, or null outside tmux. Null is a normal, connectable state. */
|
|
50
|
+
pane: string | null;
|
|
51
|
+
/** Fired ONCE, on the first successful registration. */
|
|
52
|
+
onSession?: (ctx: ConnectedContext) => void | Promise<void>;
|
|
53
|
+
/** Test seam. */
|
|
54
|
+
createClient?: (profile: ResolvedProfile) => TaskflowClient;
|
|
55
|
+
log?: (line: string) => void;
|
|
56
|
+
sleep?: (ms: number) => Promise<void>;
|
|
57
|
+
heartbeatMs?: number;
|
|
58
|
+
/** Test seam. False drives heartbeats by hand via `handle.beat()`, so a
|
|
59
|
+
* background tick cannot race an assertion. Always true in production. */
|
|
60
|
+
autoHeartbeat?: boolean;
|
|
61
|
+
}
|
|
62
|
+
export interface ConnectionHandle {
|
|
63
|
+
stop: () => void;
|
|
64
|
+
/**
|
|
65
|
+
* Resolves when the initial registration settles — and NEVER rejects.
|
|
66
|
+
*
|
|
67
|
+
* "Settles" means registered, or stopped, or the injected sleep gave up. It
|
|
68
|
+
* does NOT mean "resolves eventually": retries are unbounded, so while the
|
|
69
|
+
* backend is down this stays PENDING forever. Callers on the startup path
|
|
70
|
+
* (Task 6) must therefore not `await` it — doing so would keep the MCP
|
|
71
|
+
* server from serving a single tool call until the backend came up. Start
|
|
72
|
+
* the connection, keep the handle, carry on; only tests await it, and they
|
|
73
|
+
* inject a `sleep` with a finite budget.
|
|
74
|
+
*/
|
|
75
|
+
settled: Promise<void>;
|
|
76
|
+
/** Run one heartbeat tick now. Tests use it; production uses the loop. */
|
|
77
|
+
beat: () => Promise<void>;
|
|
78
|
+
}
|
|
79
|
+
/** The current connection status, for `whoami` to report. */
|
|
80
|
+
export declare function getConnectionStatus(): ConnectionStatus;
|
|
81
|
+
/** Record that we are deliberately NOT connecting until a human picks. */
|
|
82
|
+
export declare function setNeedsProfile(detail: string): void;
|
|
83
|
+
/** Test seam — reset module-level status between cases. */
|
|
84
|
+
export declare function resetConnectionStatus(): void;
|
|
85
|
+
/**
|
|
86
|
+
* Connect and stay connected. Never throws and never rejects: the agent must
|
|
87
|
+
* keep working whether or not the dashboard is reachable.
|
|
88
|
+
*/
|
|
89
|
+
export declare function startConnection(options: ConnectOptions): ConnectionHandle;
|