@creativeintelligence/abbie 0.1.6 → 0.1.8
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/bin/dev.js +1 -49
- package/bin/run.js +42 -49
- package/dist/cli/commands/project/add.d.ts +0 -1
- package/dist/cli/commands/project/add.js +16 -52
- package/dist/cli/commands/project/list.js +13 -93
- package/dist/cli/commands/project/remove.d.ts +0 -2
- package/dist/cli/commands/project/remove.js +11 -28
- package/dist/cli/commands/session/list.js +3 -12
- package/dist/cli/commands/session/mark-done.js +1 -7
- package/dist/cli/commands/session/start.d.ts +0 -1
- package/dist/cli/commands/session/start.js +5 -7
- package/dist/lib/active-sessions.d.ts +0 -12
- package/dist/lib/active-sessions.js +6 -175
- package/dist/lib/project-path.d.ts +6 -0
- package/dist/lib/project-path.js +21 -0
- package/dist/lib.d.ts +1 -2
- package/dist/lib.js +2 -4
- package/oclif.manifest.json +2569 -6368
- package/package.json +21 -10
- package/dist/cli/commands/backlog/add.d.ts +0 -22
- package/dist/cli/commands/backlog/add.js +0 -65
- package/dist/cli/commands/backlog/claim.d.ts +0 -19
- package/dist/cli/commands/backlog/claim.js +0 -45
- package/dist/cli/commands/backlog/complete.d.ts +0 -18
- package/dist/cli/commands/backlog/complete.js +0 -42
- package/dist/cli/commands/backlog/list.d.ts +0 -20
- package/dist/cli/commands/backlog/list.js +0 -91
- package/dist/cli/commands/backlog/pick.d.ts +0 -18
- package/dist/cli/commands/backlog/pick.js +0 -42
- package/dist/cli/commands/backlog/sync.d.ts +0 -24
- package/dist/cli/commands/backlog/sync.js +0 -109
- package/dist/cli/commands/daemon.d.ts +0 -56
- package/dist/cli/commands/daemon.js +0 -1465
- package/dist/cli/commands/docs/lint.d.ts +0 -18
- package/dist/cli/commands/docs/lint.js +0 -82
- package/dist/cli/commands/docs/sync.d.ts +0 -19
- package/dist/cli/commands/docs/sync.js +0 -76
- package/dist/cli/commands/gc.d.ts +0 -29
- package/dist/cli/commands/gc.js +0 -211
- package/dist/cli/commands/index.d.ts +0 -36
- package/dist/cli/commands/index.js +0 -228
- package/dist/cli/commands/panes/broker.d.ts +0 -17
- package/dist/cli/commands/panes/broker.js +0 -57
- package/dist/cli/commands/panes/pipe-sink.d.ts +0 -17
- package/dist/cli/commands/panes/pipe-sink.js +0 -90
- package/dist/cli/commands/panes/snapshot.d.ts +0 -20
- package/dist/cli/commands/panes/snapshot.js +0 -125
- package/dist/cli/commands/preview/init.d.ts +0 -25
- package/dist/cli/commands/preview/init.js +0 -159
- package/dist/cli/commands/preview/sync.d.ts +0 -23
- package/dist/cli/commands/preview/sync.js +0 -144
- package/dist/cli/commands/preview/watch.d.ts +0 -24
- package/dist/cli/commands/preview/watch.js +0 -153
- package/dist/cli/commands/resource/acquire.d.ts +0 -21
- package/dist/cli/commands/resource/acquire.js +0 -90
- package/dist/cli/commands/resource/list.d.ts +0 -15
- package/dist/cli/commands/resource/list.js +0 -61
- package/dist/cli/commands/resource/release.d.ts +0 -18
- package/dist/cli/commands/resource/release.js +0 -50
- package/dist/cli/commands/resource/wait.d.ts +0 -21
- package/dist/cli/commands/resource/wait.js +0 -73
- package/dist/cli/commands/session/view.d.ts +0 -24
- package/dist/cli/commands/session/view.js +0 -145
- package/dist/cli/commands/start.d.ts +0 -37
- package/dist/cli/commands/start.js +0 -234
- package/dist/cli/commands/triage/claim.d.ts +0 -23
- package/dist/cli/commands/triage/claim.js +0 -186
- package/dist/cli/commands/triage/list.d.ts +0 -22
- package/dist/cli/commands/triage/list.js +0 -112
- package/dist/cli/commands/triage/next.d.ts +0 -18
- package/dist/cli/commands/triage/next.js +0 -63
- package/dist/cli/commands/triage/pull.d.ts +0 -19
- package/dist/cli/commands/triage/pull.js +0 -82
- package/dist/cli/commands/triage/stats.d.ts +0 -16
- package/dist/cli/commands/triage/stats.js +0 -69
- package/dist/cli/commands/tunnel/list.d.ts +0 -16
- package/dist/cli/commands/tunnel/list.js +0 -98
- package/dist/cli/commands/tunnel/start.d.ts +0 -24
- package/dist/cli/commands/tunnel/start.js +0 -107
- package/dist/cli/commands/tunnel/stop.d.ts +0 -20
- package/dist/cli/commands/tunnel/stop.js +0 -90
- package/dist/cli/commands/tunnel/url.d.ts +0 -21
- package/dist/cli/commands/tunnel/url.js +0 -70
- package/dist/cli/commands/windows/context.d.ts +0 -18
- package/dist/cli/commands/windows/context.js +0 -326
- package/dist/cli/commands/windows/focus.d.ts +0 -17
- package/dist/cli/commands/windows/focus.js +0 -103
- package/dist/cli/commands/windows/list.d.ts +0 -21
- package/dist/cli/commands/windows/list.js +0 -172
- package/dist/cli/commands/windows/map.d.ts +0 -17
- package/dist/cli/commands/windows/map.js +0 -168
- package/dist/cli/commands/windows/read.d.ts +0 -21
- package/dist/cli/commands/windows/read.js +0 -241
- package/dist/cli/commands/windows/search.d.ts +0 -24
- package/dist/cli/commands/windows/search.js +0 -171
- package/dist/cli/commands/windows/show.d.ts +0 -19
- package/dist/cli/commands/windows/show.js +0 -165
- package/dist/cli/commands/windows/watch.d.ts +0 -19
- package/dist/cli/commands/windows/watch.js +0 -241
- package/dist/lib/managed-session.d.ts +0 -27
- package/dist/lib/managed-session.js +0 -105
- package/dist/lib/panes/broker.d.ts +0 -130
- package/dist/lib/panes/broker.js +0 -97
- package/dist/lib/panes/index.d.ts +0 -2
- package/dist/lib/panes/index.js +0 -1
- package/dist/lib/panes/server.d.ts +0 -17
- package/dist/lib/panes/server.js +0 -308
- package/dist/lib/preview/manager.d.ts +0 -77
- package/dist/lib/preview/manager.js +0 -369
- package/dist/lib/preview/schema.d.ts +0 -2
- package/dist/lib/preview/schema.js +0 -32
- package/dist/lib/preview/sprite.d.ts +0 -85
- package/dist/lib/preview/sprite.js +0 -321
- package/dist/lib/preview/watcher.d.ts +0 -63
- package/dist/lib/preview/watcher.js +0 -185
- package/dist/lib/project-identity.d.ts +0 -16
- package/dist/lib/project-identity.js +0 -75
- package/dist/lib/tmux/bridge.d.ts +0 -133
- package/dist/lib/tmux/bridge.js +0 -315
- package/dist/lib/tmux/context.d.ts +0 -82
- package/dist/lib/tmux/context.js +0 -239
- package/dist/lib/tmux/index.d.ts +0 -8
- package/dist/lib/tmux/index.js +0 -11
- package/dist/lib/tmux/map.d.ts +0 -57
- package/dist/lib/tmux/map.js +0 -198
- package/dist/lib/tmux/panes.d.ts +0 -27
- package/dist/lib/tmux/panes.js +0 -151
- package/dist/lib/tmux/redaction.d.ts +0 -57
- package/dist/lib/tmux/redaction.js +0 -152
- package/dist/lib/web/analytics.d.ts +0 -63
- package/dist/lib/web/analytics.js +0 -168
- package/dist/lib/web/server.d.ts +0 -26
- package/dist/lib/web/server.js +0 -697
- package/dist/lib/web/tmux-bridge.d.ts +0 -7
- package/dist/lib/web/tmux-bridge.js +0 -7
- package/dist/lib/windows/index.d.ts +0 -3
- package/dist/lib/windows/index.js +0 -2
- package/dist/lib/windows/inventory.d.ts +0 -21
- package/dist/lib/windows/inventory.js +0 -263
- package/dist/lib/windows/types.d.ts +0 -46
- package/dist/lib/windows/types.js +0 -1
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { createResourceManager } from "../../../lib/resources.js";
|
|
2
|
-
import { BaseCommand } from "../../base-command.js";
|
|
3
|
-
export default class ResourceList extends BaseCommand {
|
|
4
|
-
static description = "List resource availability and active leases";
|
|
5
|
-
static hidden = false;
|
|
6
|
-
static examples = [
|
|
7
|
-
"<%= config.bin %> resource list",
|
|
8
|
-
"<%= config.bin %> resource list --json",
|
|
9
|
-
];
|
|
10
|
-
static flags = {
|
|
11
|
-
...BaseCommand.baseFlags,
|
|
12
|
-
};
|
|
13
|
-
async execute() {
|
|
14
|
-
const { flags } = await this.parse(ResourceList);
|
|
15
|
-
this.parsedFlags = flags;
|
|
16
|
-
const resources = createResourceManager();
|
|
17
|
-
const list = resources.listResources();
|
|
18
|
-
if (flags.json || flags.format === "json") {
|
|
19
|
-
return list;
|
|
20
|
-
}
|
|
21
|
-
// NDJSON: one resource per line
|
|
22
|
-
if (this.ndjsonEnabled()) {
|
|
23
|
-
this.outputNdjson(list);
|
|
24
|
-
return list;
|
|
25
|
-
}
|
|
26
|
-
if (list.length === 0) {
|
|
27
|
-
this.logInfo("No active resource leases");
|
|
28
|
-
return [];
|
|
29
|
-
}
|
|
30
|
-
this.logInfo("Resource availability:\n");
|
|
31
|
-
for (const { resource, availability } of list) {
|
|
32
|
-
const { available, total, leases } = availability;
|
|
33
|
-
const indicator = available > 0 ? "🟢" : "🔴";
|
|
34
|
-
const capacityStr = total === -1 ? "∞" : `${available}/${total}`;
|
|
35
|
-
this.log(`${indicator} ${resource}: ${capacityStr} available`);
|
|
36
|
-
if (leases.length > 0) {
|
|
37
|
-
for (const lease of leases) {
|
|
38
|
-
const expiresIn = this.getExpiresIn(lease.expires_at);
|
|
39
|
-
this.log(` └─ ${lease.session_id} (${lease.agent}) - expires ${expiresIn}`);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
this.log("");
|
|
43
|
-
}
|
|
44
|
-
return list;
|
|
45
|
-
}
|
|
46
|
-
getExpiresIn(expiresAt) {
|
|
47
|
-
const ms = new Date(expiresAt).getTime() - Date.now();
|
|
48
|
-
if (ms < 0)
|
|
49
|
-
return "expired";
|
|
50
|
-
const seconds = Math.floor(ms / 1000);
|
|
51
|
-
const minutes = Math.floor(seconds / 60);
|
|
52
|
-
const hours = Math.floor(minutes / 60);
|
|
53
|
-
if (hours > 0) {
|
|
54
|
-
return `in ${hours}h ${minutes % 60}m`;
|
|
55
|
-
}
|
|
56
|
-
if (minutes > 0) {
|
|
57
|
-
return `in ${minutes}m`;
|
|
58
|
-
}
|
|
59
|
-
return `in ${seconds}s`;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { BaseCommand } from "../../base-command.js";
|
|
2
|
-
export default class ResourceRelease extends BaseCommand {
|
|
3
|
-
static description: string;
|
|
4
|
-
static hidden: boolean;
|
|
5
|
-
static examples: string[];
|
|
6
|
-
static args: {
|
|
7
|
-
resource: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
8
|
-
session_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
9
|
-
};
|
|
10
|
-
static flags: {
|
|
11
|
-
format: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
-
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
-
"json-errors": import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
-
ndjson: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
-
};
|
|
16
|
-
protected execute(): Promise<unknown>;
|
|
17
|
-
}
|
|
18
|
-
//# sourceMappingURL=release.d.ts.map
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { Args } from "@oclif/core";
|
|
2
|
-
import { createEvent, EventStore } from "../../../lib/events.js";
|
|
3
|
-
import { createResourceManager, DEFAULT_RESOURCES } from "../../../lib/resources.js";
|
|
4
|
-
import { BaseCommand } from "../../base-command.js";
|
|
5
|
-
export default class ResourceRelease extends BaseCommand {
|
|
6
|
-
static description = "Release a resource lease";
|
|
7
|
-
static hidden = false;
|
|
8
|
-
static examples = [
|
|
9
|
-
"<%= config.bin %> resource release codex_api SESSION_ID",
|
|
10
|
-
"<%= config.bin %> resource release ngrok_tunnel SESSION_ID --json",
|
|
11
|
-
];
|
|
12
|
-
static args = {
|
|
13
|
-
resource: Args.string({
|
|
14
|
-
description: "Resource name",
|
|
15
|
-
required: true,
|
|
16
|
-
options: Object.keys(DEFAULT_RESOURCES),
|
|
17
|
-
}),
|
|
18
|
-
session_id: Args.string({
|
|
19
|
-
description: "Session ID holding the resource",
|
|
20
|
-
required: true,
|
|
21
|
-
}),
|
|
22
|
-
};
|
|
23
|
-
static flags = {
|
|
24
|
-
...BaseCommand.baseFlags,
|
|
25
|
-
};
|
|
26
|
-
async execute() {
|
|
27
|
-
const { args, flags } = await this.parse(ResourceRelease);
|
|
28
|
-
this.parsedFlags = flags;
|
|
29
|
-
const resources = createResourceManager();
|
|
30
|
-
const events = new EventStore();
|
|
31
|
-
const released = resources.release(args.resource, args.session_id);
|
|
32
|
-
if (!released) {
|
|
33
|
-
if (flags.json || flags.format === "json") {
|
|
34
|
-
return { success: false, reason: "Lease not found" };
|
|
35
|
-
}
|
|
36
|
-
this.logInfo(`No active lease found for ${args.session_id} on ${args.resource}`);
|
|
37
|
-
return { success: false };
|
|
38
|
-
}
|
|
39
|
-
// Emit resource released event
|
|
40
|
-
events.append(createEvent.resourceReleased(args.session_id, "claude", {
|
|
41
|
-
resource: args.resource,
|
|
42
|
-
lease_id: `${args.session_id}-${args.resource}`,
|
|
43
|
-
}));
|
|
44
|
-
if (flags.json || flags.format === "json") {
|
|
45
|
-
return { success: true };
|
|
46
|
-
}
|
|
47
|
-
this.logInfo(`Released ${args.resource} for ${args.session_id}`);
|
|
48
|
-
return { success: true };
|
|
49
|
-
}
|
|
50
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { BaseCommand } from "../../base-command.js";
|
|
2
|
-
export default class ResourceWait extends BaseCommand {
|
|
3
|
-
static description: string;
|
|
4
|
-
static hidden: boolean;
|
|
5
|
-
static examples: string[];
|
|
6
|
-
static args: {
|
|
7
|
-
resource: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
8
|
-
session_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
9
|
-
};
|
|
10
|
-
static flags: {
|
|
11
|
-
agent: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
-
"max-wait": import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
-
ttl: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
-
format: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
-
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
-
"json-errors": import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
|
-
ndjson: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
18
|
-
};
|
|
19
|
-
protected execute(): Promise<unknown>;
|
|
20
|
-
}
|
|
21
|
-
//# sourceMappingURL=wait.d.ts.map
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { Args, Flags } from "@oclif/core";
|
|
2
|
-
import { createEvent, EventStore } from "../../../lib/events.js";
|
|
3
|
-
import { createResourceManager, DEFAULT_RESOURCES } from "../../../lib/resources.js";
|
|
4
|
-
import { AGENTS } from "../../../lib/types.js";
|
|
5
|
-
import { BaseCommand } from "../../base-command.js";
|
|
6
|
-
export default class ResourceWait extends BaseCommand {
|
|
7
|
-
static description = "Wait for a resource to become available and acquire it";
|
|
8
|
-
static hidden = false;
|
|
9
|
-
static examples = [
|
|
10
|
-
"<%= config.bin %> resource wait codex_api SESSION_ID --agent claude",
|
|
11
|
-
"<%= config.bin %> resource wait codex_api SESSION_ID --agent claude --max-wait 300000",
|
|
12
|
-
"<%= config.bin %> resource wait ngrok_tunnel SESSION_ID --agent codex --json",
|
|
13
|
-
];
|
|
14
|
-
static args = {
|
|
15
|
-
resource: Args.string({
|
|
16
|
-
description: "Resource name",
|
|
17
|
-
required: true,
|
|
18
|
-
options: Object.keys(DEFAULT_RESOURCES),
|
|
19
|
-
}),
|
|
20
|
-
session_id: Args.string({
|
|
21
|
-
description: "Session ID requesting the resource",
|
|
22
|
-
required: true,
|
|
23
|
-
}),
|
|
24
|
-
};
|
|
25
|
-
static flags = {
|
|
26
|
-
...BaseCommand.baseFlags,
|
|
27
|
-
agent: Flags.string({
|
|
28
|
-
char: "a",
|
|
29
|
-
description: "Agent type",
|
|
30
|
-
required: true,
|
|
31
|
-
options: [...AGENTS],
|
|
32
|
-
}),
|
|
33
|
-
"max-wait": Flags.integer({
|
|
34
|
-
description: "Maximum wait time in milliseconds",
|
|
35
|
-
default: 300000, // 5 minutes
|
|
36
|
-
}),
|
|
37
|
-
ttl: Flags.integer({
|
|
38
|
-
char: "t",
|
|
39
|
-
description: "Time-to-live for acquired lease in milliseconds",
|
|
40
|
-
}),
|
|
41
|
-
};
|
|
42
|
-
async execute() {
|
|
43
|
-
const { args, flags } = await this.parse(ResourceWait);
|
|
44
|
-
this.parsedFlags = flags;
|
|
45
|
-
const resources = createResourceManager();
|
|
46
|
-
const events = new EventStore();
|
|
47
|
-
if (!(flags.json || flags.format === "json")) {
|
|
48
|
-
this.logInfo(`Waiting for ${args.resource} (max ${flags["max-wait"]}ms)...`);
|
|
49
|
-
}
|
|
50
|
-
const lease = await resources.waitForResource(args.resource, args.session_id, flags.agent, {
|
|
51
|
-
maxWaitMs: flags["max-wait"],
|
|
52
|
-
ttlMs: flags.ttl,
|
|
53
|
-
});
|
|
54
|
-
if (!lease) {
|
|
55
|
-
if (flags.json || flags.format === "json") {
|
|
56
|
-
return { success: false, reason: "Timeout waiting for resource" };
|
|
57
|
-
}
|
|
58
|
-
this.logInfo(`Timeout waiting for ${args.resource}`);
|
|
59
|
-
return { success: false };
|
|
60
|
-
}
|
|
61
|
-
// Emit resource acquired event
|
|
62
|
-
events.append(createEvent.resourceAcquired(args.session_id, flags.agent, {
|
|
63
|
-
resource: args.resource,
|
|
64
|
-
lease_id: lease.lease_id,
|
|
65
|
-
expires_at: lease.expires_at,
|
|
66
|
-
}));
|
|
67
|
-
if (flags.json || flags.format === "json") {
|
|
68
|
-
return { success: true, lease };
|
|
69
|
-
}
|
|
70
|
-
this.log(lease.lease_id);
|
|
71
|
-
return { success: true, lease };
|
|
72
|
-
}
|
|
73
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { BaseCommand } from "../../base-command.js";
|
|
2
|
-
export default class SessionView extends BaseCommand {
|
|
3
|
-
static description: string;
|
|
4
|
-
static hidden: boolean;
|
|
5
|
-
static examples: string[];
|
|
6
|
-
static args: {
|
|
7
|
-
session_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
8
|
-
};
|
|
9
|
-
static flags: {
|
|
10
|
-
"tmux-session": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
-
format: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
-
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
-
"json-errors": import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
-
ndjson: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
-
};
|
|
16
|
-
protected execute(): Promise<unknown>;
|
|
17
|
-
private ensureFile;
|
|
18
|
-
private shellEscape;
|
|
19
|
-
private runTmux;
|
|
20
|
-
private hasProjectWindow;
|
|
21
|
-
private openInNvimIfAvailable;
|
|
22
|
-
private openInTmuxPane;
|
|
23
|
-
}
|
|
24
|
-
//# sourceMappingURL=view.d.ts.map
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
-
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { dirname } from "node:path";
|
|
4
|
-
import { Args, Flags } from "@oclif/core";
|
|
5
|
-
import { getActiveSessionManager } from "../../../lib/active-sessions.js";
|
|
6
|
-
import { ensureManagedSession } from "../../../lib/managed-session.js";
|
|
7
|
-
import { getNvimInstanceForWindow } from "../../../lib/nvim/discovery.js";
|
|
8
|
-
import { isServerReachable, remoteSend } from "../../../lib/nvim/remote.js";
|
|
9
|
-
import { buildProcessTree, getProcessSnapshot } from "../../../lib/process/snapshot.js";
|
|
10
|
-
import { getSessionLogPaths } from "../../../lib/session-result.js";
|
|
11
|
-
import { getPaneId, listWindows } from "../../../lib/tmux/bridge.js";
|
|
12
|
-
import { BaseCommand } from "../../base-command.js";
|
|
13
|
-
export default class SessionView extends BaseCommand {
|
|
14
|
-
static description = "Attach a viewer to a session's runner stdout log";
|
|
15
|
-
static hidden = false;
|
|
16
|
-
static examples = [
|
|
17
|
-
"<%= config.bin %> session view hostagent-20241225-120000-abc1",
|
|
18
|
-
"<%= config.bin %> session view hostagent-20241225-120000-abc1 --tmux-session abbie",
|
|
19
|
-
];
|
|
20
|
-
static args = {
|
|
21
|
-
session_id: Args.string({
|
|
22
|
-
description: "Session ID to view",
|
|
23
|
-
required: true,
|
|
24
|
-
}),
|
|
25
|
-
};
|
|
26
|
-
static flags = {
|
|
27
|
-
...BaseCommand.baseFlags,
|
|
28
|
-
"tmux-session": Flags.string({
|
|
29
|
-
description: 'Tmux session name (default: managed "abbie" session)',
|
|
30
|
-
}),
|
|
31
|
-
};
|
|
32
|
-
async execute() {
|
|
33
|
-
const { args, flags } = await this.parse(SessionView);
|
|
34
|
-
this.parsedFlags = flags;
|
|
35
|
-
const manager = getActiveSessionManager();
|
|
36
|
-
const session = await manager.getAsync(args.session_id);
|
|
37
|
-
if (!session) {
|
|
38
|
-
this.error(`Session not found: ${args.session_id}`);
|
|
39
|
-
}
|
|
40
|
-
const stdoutPath = session.stdout_path || getSessionLogPaths(args.session_id).stdoutPath;
|
|
41
|
-
this.ensureFile(stdoutPath);
|
|
42
|
-
const tmuxSession = flags["tmux-session"] || (await ensureManagedSession());
|
|
43
|
-
const projectWindowExists = await this.hasProjectWindow(tmuxSession, session.project);
|
|
44
|
-
const attachedInNvim = await this.openInNvimIfAvailable(projectWindowExists, session.project, tmuxSession, stdoutPath);
|
|
45
|
-
if (attachedInNvim) {
|
|
46
|
-
const payload = {
|
|
47
|
-
session_id: args.session_id,
|
|
48
|
-
project: session.project,
|
|
49
|
-
method: "nvim-tab",
|
|
50
|
-
stdout_path: stdoutPath,
|
|
51
|
-
tmux_session: tmuxSession,
|
|
52
|
-
};
|
|
53
|
-
if (flags.json || flags.format === "json") {
|
|
54
|
-
return payload;
|
|
55
|
-
}
|
|
56
|
-
this.log(`Attached via nvim tab: tail -F ${stdoutPath}`);
|
|
57
|
-
return payload;
|
|
58
|
-
}
|
|
59
|
-
await this.openInTmuxPane(projectWindowExists, session.project, session.cwd, tmuxSession, stdoutPath, args.session_id);
|
|
60
|
-
const payload = {
|
|
61
|
-
session_id: args.session_id,
|
|
62
|
-
project: session.project,
|
|
63
|
-
method: "tmux-pane",
|
|
64
|
-
stdout_path: stdoutPath,
|
|
65
|
-
tmux_session: tmuxSession,
|
|
66
|
-
};
|
|
67
|
-
if (flags.json || flags.format === "json") {
|
|
68
|
-
return payload;
|
|
69
|
-
}
|
|
70
|
-
this.log(`Attached via tmux pane: tail -F ${stdoutPath}`);
|
|
71
|
-
return payload;
|
|
72
|
-
}
|
|
73
|
-
ensureFile(path) {
|
|
74
|
-
if (existsSync(path))
|
|
75
|
-
return;
|
|
76
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
77
|
-
writeFileSync(path, "", { flag: "a" });
|
|
78
|
-
}
|
|
79
|
-
shellEscape(value) {
|
|
80
|
-
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
81
|
-
}
|
|
82
|
-
async runTmux(args) {
|
|
83
|
-
await new Promise((resolve, reject) => {
|
|
84
|
-
const proc = spawn("tmux", args, { stdio: ["ignore", "ignore", "ignore"] });
|
|
85
|
-
proc.on("close", (code) => {
|
|
86
|
-
if (code === 0)
|
|
87
|
-
resolve();
|
|
88
|
-
else
|
|
89
|
-
reject(new Error(`tmux ${args.join(" ")} failed with code ${code}`));
|
|
90
|
-
});
|
|
91
|
-
proc.on("error", reject);
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
async hasProjectWindow(tmuxSession, project) {
|
|
95
|
-
const windows = await listWindows(tmuxSession);
|
|
96
|
-
return windows.some((window) => window.name === project);
|
|
97
|
-
}
|
|
98
|
-
async openInNvimIfAvailable(projectWindowExists, project, tmuxSession, stdoutPath) {
|
|
99
|
-
if (!projectWindowExists)
|
|
100
|
-
return false;
|
|
101
|
-
const paneId = await getPaneId(project, 0, tmuxSession);
|
|
102
|
-
if (!paneId)
|
|
103
|
-
return false;
|
|
104
|
-
const panePidRaw = spawnSync("tmux", ["display-message", "-p", "-t", paneId, "#{pane_pid}"], {
|
|
105
|
-
encoding: "utf8",
|
|
106
|
-
}).stdout.trim();
|
|
107
|
-
const panePid = parseInt(panePidRaw, 10);
|
|
108
|
-
if (!(panePid > 0))
|
|
109
|
-
return false;
|
|
110
|
-
const snapshot = await getProcessSnapshot();
|
|
111
|
-
const tree = buildProcessTree(snapshot);
|
|
112
|
-
const nvimInstance = await getNvimInstanceForWindow(panePid, snapshot, tree);
|
|
113
|
-
if (!nvimInstance?.serverAddr)
|
|
114
|
-
return false;
|
|
115
|
-
const reachable = await isServerReachable(nvimInstance.serverAddr);
|
|
116
|
-
if (!reachable)
|
|
117
|
-
return false;
|
|
118
|
-
const tailCommand = `tail -F ${this.shellEscape(stdoutPath)}`;
|
|
119
|
-
return remoteSend(nvimInstance.serverAddr, `:tabnew | terminal ${tailCommand}\n`);
|
|
120
|
-
}
|
|
121
|
-
async openInTmuxPane(projectWindowExists, project, cwd, tmuxSession, stdoutPath, sessionId) {
|
|
122
|
-
const tailCommand = `tail -F ${this.shellEscape(stdoutPath)}`;
|
|
123
|
-
if (projectWindowExists) {
|
|
124
|
-
await this.runTmux([
|
|
125
|
-
"split-window",
|
|
126
|
-
"-t",
|
|
127
|
-
`${tmuxSession}:${project}`,
|
|
128
|
-
"-c",
|
|
129
|
-
cwd,
|
|
130
|
-
tailCommand,
|
|
131
|
-
]);
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
await this.runTmux([
|
|
135
|
-
"new-window",
|
|
136
|
-
"-t",
|
|
137
|
-
`${tmuxSession}:`,
|
|
138
|
-
"-n",
|
|
139
|
-
`${project}-view-${sessionId.slice(-4)}`,
|
|
140
|
-
"-c",
|
|
141
|
-
cwd,
|
|
142
|
-
tailCommand,
|
|
143
|
-
]);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* abbie (default command) — the single entry point.
|
|
3
|
-
*
|
|
4
|
-
* First run:
|
|
5
|
-
* 1. Check Pi → not found → auto-install
|
|
6
|
-
* 2. Install Abbie Pi extension
|
|
7
|
-
* 3. Open browser for Clerk auth
|
|
8
|
-
* 4. Connect AI provider (OAuth — uses your subscription)
|
|
9
|
-
* 5. Launch Pi (extension auto-starts bridge)
|
|
10
|
-
*
|
|
11
|
-
* Subsequent runs:
|
|
12
|
-
* Launch Pi directly (extension handles bridge)
|
|
13
|
-
*/
|
|
14
|
-
import { BaseCommand } from "../base-command.js";
|
|
15
|
-
export default class DefaultCommand extends BaseCommand {
|
|
16
|
-
static id: string;
|
|
17
|
-
static summary: string;
|
|
18
|
-
static description: string;
|
|
19
|
-
static examples: string[];
|
|
20
|
-
static flags: {
|
|
21
|
-
setup: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
22
|
-
status: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
23
|
-
format: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
24
|
-
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
25
|
-
"json-errors": import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
26
|
-
ndjson: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
27
|
-
};
|
|
28
|
-
static strict: boolean;
|
|
29
|
-
execute(): Promise<unknown>;
|
|
30
|
-
private runSetup;
|
|
31
|
-
/**
|
|
32
|
-
* Find the bundled extension source directory.
|
|
33
|
-
* Looks in several locations depending on how the CLI is installed.
|
|
34
|
-
*/
|
|
35
|
-
private findExtensionSource;
|
|
36
|
-
}
|
|
37
|
-
//# sourceMappingURL=start.d.ts.map
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* abbie (default command) — the single entry point.
|
|
3
|
-
*
|
|
4
|
-
* First run:
|
|
5
|
-
* 1. Check Pi → not found → auto-install
|
|
6
|
-
* 2. Install Abbie Pi extension
|
|
7
|
-
* 3. Open browser for Clerk auth
|
|
8
|
-
* 4. Connect AI provider (OAuth — uses your subscription)
|
|
9
|
-
* 5. Launch Pi (extension auto-starts bridge)
|
|
10
|
-
*
|
|
11
|
-
* Subsequent runs:
|
|
12
|
-
* Launch Pi directly (extension handles bridge)
|
|
13
|
-
*/
|
|
14
|
-
import { existsSync, mkdirSync, cpSync, readFileSync } from "node:fs";
|
|
15
|
-
import { homedir } from "node:os";
|
|
16
|
-
import { join, dirname } from "node:path";
|
|
17
|
-
import { fileURLToPath } from "node:url";
|
|
18
|
-
import { spawnSync, spawn, execSync } from "node:child_process";
|
|
19
|
-
import { Flags } from "@oclif/core";
|
|
20
|
-
import { BaseCommand } from "../base-command.js";
|
|
21
|
-
import { hasAnyProvider, ensureProviderConnected, getConnectedProviders } from "../../lib/provider-auth.js";
|
|
22
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
23
|
-
const __dirname = dirname(__filename);
|
|
24
|
-
const PI_PACKAGE = "@mariozechner/pi-coding-agent";
|
|
25
|
-
const ABBIE_HOME = join(homedir(), ".abbie");
|
|
26
|
-
const PI_AGENT_DIR = join(homedir(), ".pi", "agent");
|
|
27
|
-
const PI_EXT_DIR = join(PI_AGENT_DIR, "extensions", "abbie");
|
|
28
|
-
function piInstalled() {
|
|
29
|
-
try {
|
|
30
|
-
const result = spawnSync("pi", ["--version"], { encoding: "utf8", timeout: 5000 });
|
|
31
|
-
if (result.status === 0 && result.stdout?.trim()) {
|
|
32
|
-
return { installed: true, version: result.stdout.trim(), path: "pi" };
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
catch { /* not found */ }
|
|
36
|
-
return { installed: false };
|
|
37
|
-
}
|
|
38
|
-
function abbieConfigured() {
|
|
39
|
-
const configPath = join(ABBIE_HOME, "config.json");
|
|
40
|
-
if (!existsSync(configPath))
|
|
41
|
-
return false;
|
|
42
|
-
try {
|
|
43
|
-
const cfg = JSON.parse(readFileSync(configPath, "utf8"));
|
|
44
|
-
// Must have clerkId — convexUrl alone isn't enough (user may have logged out)
|
|
45
|
-
return Boolean(cfg.clerkId);
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
function extensionInstalled() {
|
|
52
|
-
return existsSync(join(PI_EXT_DIR, "index.ts")) || existsSync(join(PI_EXT_DIR, "index.js"));
|
|
53
|
-
}
|
|
54
|
-
export default class DefaultCommand extends BaseCommand {
|
|
55
|
-
static id = "";
|
|
56
|
-
static summary = "Launch Abbie";
|
|
57
|
-
static description = `Start an agent session powered by Pi with cloud sync via Abbie.
|
|
58
|
-
|
|
59
|
-
On first run, walks you through setup:
|
|
60
|
-
• Installs Pi (the coding agent runtime) if needed
|
|
61
|
-
• Installs the Abbie extension into Pi
|
|
62
|
-
• Authenticates with your Abbie account
|
|
63
|
-
• Launches Pi with the bridge auto-connected
|
|
64
|
-
|
|
65
|
-
After setup, just run \`abbie\` to start working.`;
|
|
66
|
-
static examples = [
|
|
67
|
-
"$ abbie",
|
|
68
|
-
"$ abbie --setup",
|
|
69
|
-
"$ abbie --status",
|
|
70
|
-
];
|
|
71
|
-
static flags = {
|
|
72
|
-
...BaseCommand.baseFlags,
|
|
73
|
-
setup: Flags.boolean({
|
|
74
|
-
description: "Force re-run the setup wizard",
|
|
75
|
-
default: false,
|
|
76
|
-
}),
|
|
77
|
-
status: Flags.boolean({
|
|
78
|
-
description: "Show setup status and exit",
|
|
79
|
-
default: false,
|
|
80
|
-
}),
|
|
81
|
-
};
|
|
82
|
-
// Pass-through: any extra args go to pi
|
|
83
|
-
static strict = false;
|
|
84
|
-
async execute() {
|
|
85
|
-
const { flags, argv } = await this.parse(DefaultCommand);
|
|
86
|
-
this.parsedFlags = flags;
|
|
87
|
-
const pi = piInstalled();
|
|
88
|
-
const configured = abbieConfigured();
|
|
89
|
-
const extOk = extensionInstalled();
|
|
90
|
-
// --status: show state and exit
|
|
91
|
-
if (flags.status) {
|
|
92
|
-
const providers = getConnectedProviders();
|
|
93
|
-
this.log("");
|
|
94
|
-
this.log(" abbie status");
|
|
95
|
-
this.log("");
|
|
96
|
-
this.log(` pi: ${pi.installed ? `✓ ${pi.version}` : "✗ not installed"}`);
|
|
97
|
-
this.log(` extension: ${extOk ? "✓ installed" : "✗ not installed"}`);
|
|
98
|
-
this.log(` auth: ${configured ? "✓ configured" : "✗ not configured"}`);
|
|
99
|
-
this.log(` providers: ${providers.length > 0 ? `✓ ${providers.join(", ")}` : "✗ none connected"}`);
|
|
100
|
-
this.log("");
|
|
101
|
-
return { pi: pi.installed, extension: extOk, auth: configured, providers };
|
|
102
|
-
}
|
|
103
|
-
// Setup needed?
|
|
104
|
-
const providerOk = hasAnyProvider();
|
|
105
|
-
const needsSetup = flags.setup || !pi.installed || !configured || !extOk || !providerOk;
|
|
106
|
-
if (needsSetup) {
|
|
107
|
-
await this.runSetup(pi, configured, extOk);
|
|
108
|
-
// Re-check after setup
|
|
109
|
-
const piNow = piInstalled();
|
|
110
|
-
if (!piNow.installed) {
|
|
111
|
-
this.error("Pi installation failed. Install manually:\n\n npm install -g @mariozechner/pi-coding-agent\n");
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
// Launch Pi with any pass-through args
|
|
115
|
-
this.log("");
|
|
116
|
-
this.log(" launching pi...");
|
|
117
|
-
this.log("");
|
|
118
|
-
const piArgs = argv.length > 0 ? argv : [];
|
|
119
|
-
const child = spawn("pi", piArgs, {
|
|
120
|
-
stdio: "inherit",
|
|
121
|
-
env: process.env,
|
|
122
|
-
cwd: process.cwd(),
|
|
123
|
-
});
|
|
124
|
-
process.on("SIGINT", () => child.kill("SIGINT"));
|
|
125
|
-
process.on("SIGTERM", () => child.kill("SIGTERM"));
|
|
126
|
-
return new Promise((resolve) => {
|
|
127
|
-
child.on("exit", (code) => {
|
|
128
|
-
resolve({ exitCode: code ?? 0 });
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
async runSetup(pi, configured, extOk) {
|
|
133
|
-
this.log("");
|
|
134
|
-
this.log(" abbie setup");
|
|
135
|
-
this.log(" ──────────");
|
|
136
|
-
this.log("");
|
|
137
|
-
// Step 1: Install Pi
|
|
138
|
-
if (!pi.installed) {
|
|
139
|
-
this.log(" [1/4] installing pi...");
|
|
140
|
-
this.log(` npm install -g ${PI_PACKAGE}`);
|
|
141
|
-
this.log("");
|
|
142
|
-
try {
|
|
143
|
-
execSync(`npm install -g ${PI_PACKAGE}`, {
|
|
144
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
145
|
-
encoding: "utf8",
|
|
146
|
-
timeout: 120_000,
|
|
147
|
-
});
|
|
148
|
-
const check = piInstalled();
|
|
149
|
-
if (check.installed) {
|
|
150
|
-
this.log(` ✓ pi ${check.version} installed`);
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
this.error("Pi install completed but binary not found. Check your PATH.");
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
catch (err) {
|
|
157
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
158
|
-
this.logWarn(`auto-install failed: ${msg}`);
|
|
159
|
-
this.log("");
|
|
160
|
-
this.log(" install manually:");
|
|
161
|
-
this.log(` npm install -g ${PI_PACKAGE}`);
|
|
162
|
-
this.log("");
|
|
163
|
-
this.error("Pi is required to continue.");
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
this.log(` [1/4] pi ${pi.version} ✓`);
|
|
168
|
-
}
|
|
169
|
-
this.log("");
|
|
170
|
-
// Step 2: Install Abbie extension
|
|
171
|
-
if (!extOk) {
|
|
172
|
-
this.log(" [2/4] installing abbie extension...");
|
|
173
|
-
// Find the bundled extension
|
|
174
|
-
const extSource = this.findExtensionSource();
|
|
175
|
-
if (extSource) {
|
|
176
|
-
mkdirSync(PI_EXT_DIR, { recursive: true });
|
|
177
|
-
cpSync(extSource, PI_EXT_DIR, { recursive: true });
|
|
178
|
-
this.log(" ✓ extension installed");
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
this.logWarn("extension source not found — the abbie tools won't be available in Pi");
|
|
182
|
-
this.logWarn("this is non-fatal; you can install it manually later");
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
this.log(" [2/4] extension ✓");
|
|
187
|
-
}
|
|
188
|
-
this.log("");
|
|
189
|
-
// Step 3: Authenticate
|
|
190
|
-
if (!configured) {
|
|
191
|
-
this.log(" [3/4] authenticating...");
|
|
192
|
-
this.log(" opening browser for login...");
|
|
193
|
-
this.log("");
|
|
194
|
-
// Delegate to the login command
|
|
195
|
-
try {
|
|
196
|
-
await this.config.runCommand("login", []);
|
|
197
|
-
}
|
|
198
|
-
catch {
|
|
199
|
-
this.logWarn("login failed or was cancelled — you can run `abbie login` later");
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
else {
|
|
203
|
-
this.log(" [3/4] authenticated ✓");
|
|
204
|
-
}
|
|
205
|
-
this.log("");
|
|
206
|
-
// Step 4: Connect AI provider
|
|
207
|
-
await ensureProviderConnected((msg) => this.log(msg));
|
|
208
|
-
this.log("");
|
|
209
|
-
this.log(" setup complete ✓");
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Find the bundled extension source directory.
|
|
213
|
-
* Looks in several locations depending on how the CLI is installed.
|
|
214
|
-
*/
|
|
215
|
-
findExtensionSource() {
|
|
216
|
-
const candidates = [
|
|
217
|
-
// When installed globally via npm — extension bundled in package
|
|
218
|
-
join(__dirname, "..", "..", "..", "extensions", "abbie"),
|
|
219
|
-
// When running from the monorepo
|
|
220
|
-
join(process.cwd(), "extensions", "abbie"),
|
|
221
|
-
// The canonical location (might already exist from manual setup)
|
|
222
|
-
join(homedir(), ".pi", "agent", "extensions", "abbie"),
|
|
223
|
-
];
|
|
224
|
-
for (const candidate of candidates) {
|
|
225
|
-
if (existsSync(join(candidate, "index.ts")) || existsSync(join(candidate, "index.js"))) {
|
|
226
|
-
// Don't "find" the target as a source — that's circular
|
|
227
|
-
if (candidate === PI_EXT_DIR)
|
|
228
|
-
continue;
|
|
229
|
-
return candidate;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
return null;
|
|
233
|
-
}
|
|
234
|
-
}
|